/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"vm/Debugger-inl.h"#include"mozilla/DebugOnly.h"#include"mozilla/ScopeExit.h"#include"mozilla/Sprintf.h"#include"mozilla/TypeTraits.h"#include"jscntxt.h"#include"jscompartment.h"#include"jsfriendapi.h"#include"jshashutil.h"#include"jsnum.h"#include"jsobj.h"#include"jsprf.h"#include"jswrapper.h"#include"frontend/BytecodeCompiler.h"#include"frontend/Parser.h"#include"gc/Marking.h"#include"gc/Policy.h"#include"jit/BaselineDebugModeOSR.h"#include"jit/BaselineJIT.h"#include"js/Date.h"#include"js/GCAPI.h"#include"js/UbiNodeBreadthFirst.h"#include"js/Vector.h"#include"proxy/ScriptedProxyHandler.h"#include"vm/ArgumentsObject.h"#include"vm/DebuggerMemory.h"#include"vm/GeckoProfiler.h"#include"vm/GeneratorObject.h"#include"vm/TraceLogging.h"#include"vm/WrapperObject.h"#include"wasm/WasmInstance.h"#include"jsgcinlines.h"#include"jsobjinlines.h"#include"jsopcodeinlines.h"#include"jsscriptinlines.h"#include"vm/GeckoProfiler-inl.h"#include"vm/NativeObject-inl.h"#include"vm/Stack-inl.h"usingnamespacejs;usingJS::dbg::AutoEntryMonitor;usingJS::dbg::Builder;usingjs::frontend::IsIdentifier;usingmozilla::ArrayLength;usingmozilla::DebugOnly;usingmozilla::MakeScopeExit;usingmozilla::Maybe;usingmozilla::Some;usingmozilla::Nothing;usingmozilla::Variant;usingmozilla::AsVariant;usingmozilla::TimeDuration;usingmozilla::TimeStamp;/*** Forward declarations, ClassOps and Classes **************************************************/staticvoidDebuggerFrame_finalize(FreeOp*fop,JSObject*obj);staticvoidDebuggerFrame_trace(JSTracer*trc,JSObject*obj);staticvoidDebuggerEnv_trace(JSTracer*trc,JSObject*obj);staticvoidDebuggerObject_trace(JSTracer*trc,JSObject*obj);staticvoidDebuggerScript_trace(JSTracer*trc,JSObject*obj);staticvoidDebuggerSource_trace(JSTracer*trc,JSObject*obj);enum{JSSLOT_DEBUGFRAME_OWNER,JSSLOT_DEBUGFRAME_ARGUMENTS,JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,JSSLOT_DEBUGFRAME_ONPOP_HANDLER,JSSLOT_DEBUGFRAME_COUNT};constClassOpsDebuggerFrame::classOps_={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* newEnumerate */nullptr,/* resolve */nullptr,/* mayResolve */DebuggerFrame_finalize,nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */DebuggerFrame_trace};constClassDebuggerFrame::class_={"Frame",JSCLASS_HAS_PRIVATE|JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT)|JSCLASS_BACKGROUND_FINALIZE,&DebuggerFrame::classOps_};enum{JSSLOT_DEBUGARGUMENTS_FRAME,JSSLOT_DEBUGARGUMENTS_COUNT};constClassDebuggerArguments::class_={"Arguments",JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)};constClassOpsDebuggerEnvironment::classOps_={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* newEnumerate */nullptr,/* resolve */nullptr,/* mayResolve */nullptr,/* finalize */nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */DebuggerEnv_trace};constClassDebuggerEnvironment::class_={"Environment",JSCLASS_HAS_PRIVATE|JSCLASS_HAS_RESERVED_SLOTS(DebuggerEnvironment::RESERVED_SLOTS),&classOps_};enum{JSSLOT_DEBUGOBJECT_OWNER,JSSLOT_DEBUGOBJECT_COUNT};constClassOpsDebuggerObject::classOps_={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* newEnumerate */nullptr,/* resolve */nullptr,/* mayResolve */nullptr,/* finalize */nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */DebuggerObject_trace};constClassDebuggerObject::class_={"Object",JSCLASS_HAS_PRIVATE|JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),&classOps_};enum{JSSLOT_DEBUGSCRIPT_OWNER,JSSLOT_DEBUGSCRIPT_COUNT};staticconstClassOpsDebuggerScript_classOps={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* newEnumerate */nullptr,/* resolve */nullptr,/* mayResolve */nullptr,/* finalize */nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */DebuggerScript_trace};staticconstClassDebuggerScript_class={"Script",JSCLASS_HAS_PRIVATE|JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),&DebuggerScript_classOps};enum{JSSLOT_DEBUGSOURCE_OWNER,JSSLOT_DEBUGSOURCE_TEXT,JSSLOT_DEBUGSOURCE_COUNT};staticconstClassOpsDebuggerSource_classOps={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* newEnumerate */nullptr,/* resolve */nullptr,/* mayResolve */nullptr,/* finalize */nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */DebuggerSource_trace};staticconstClassDebuggerSource_class={"Source",JSCLASS_HAS_PRIVATE|JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),&DebuggerSource_classOps};/*** Utils ***************************************************************************************/staticinlineboolEnsureFunctionHasScript(JSContext*cx,HandleFunctionfun){if(fun->isInterpretedLazy()){AutoCompartmentac(cx,fun);return!!JSFunction::getOrCreateScript(cx,fun);}returntrue;}staticinlineJSScript*GetOrCreateFunctionScript(JSContext*cx,HandleFunctionfun){MOZ_ASSERT(fun->isInterpreted());if(!EnsureFunctionHasScript(cx,fun))returnnullptr;returnfun->nonLazyScript();}staticboolValueToIdentifier(JSContext*cx,HandleValuev,MutableHandleIdid){if(!ValueToId<CanGC>(cx,v,id))returnfalse;if(!JSID_IS_ATOM(id)||!IsIdentifier(JSID_TO_ATOM(id))){RootedValueval(cx,v);ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_UNEXPECTED_TYPE,JSDVG_SEARCH_STACK,val,nullptr,"not an identifier",nullptr);returnfalse;}returntrue;}classAutoRestoreCompartmentDebugMode{JSCompartment*comp_;unsignedbits_;public:explicitAutoRestoreCompartmentDebugMode(JSCompartment*comp):comp_(comp),bits_(comp->debugModeBits){MOZ_ASSERT(comp_);}~AutoRestoreCompartmentDebugMode(){if(comp_)comp_->debugModeBits=bits_;}voidrelease(){comp_=nullptr;}};// Given a Debugger instance dbg, if it is enabled, prevents all its debuggee// compartments from executing scripts. Attempts to run script will throw an// instance of Debugger.DebuggeeWouldRun from the topmost locked Debugger's// compartment.classMOZ_RAIIjs::EnterDebuggeeNoExecute{friendclassjs::LeaveDebuggeeNoExecute;Debugger&dbg_;EnterDebuggeeNoExecute**stack_;EnterDebuggeeNoExecute*prev_;// Non-nullptr when unlocked temporarily by a LeaveDebuggeeNoExecute.LeaveDebuggeeNoExecute*unlocked_;// When DebuggeeWouldRun is a warning instead of an error, whether we've// reported a warning already.boolreported_;public:explicitEnterDebuggeeNoExecute(JSContext*cx,Debugger&dbg):dbg_(dbg),unlocked_(nullptr),reported_(false){stack_=&cx->noExecuteDebuggerTop.ref();prev_=*stack_;*stack_=this;}~EnterDebuggeeNoExecute(){MOZ_ASSERT(*stack_==this);*stack_=prev_;}Debugger&debugger()const{returndbg_;}#ifdef DEBUGstaticboolisLockedInStack(JSContext*cx,Debugger&dbg){for(EnterDebuggeeNoExecute*it=cx->noExecuteDebuggerTop;it;it=it->prev_){if(&it->debugger()==&dbg)return!it->unlocked_;}returnfalse;}#endif// Given a JSContext entered into a debuggee compartment, find the lock// that locks it. Returns nullptr if not found.staticEnterDebuggeeNoExecute*findInStack(JSContext*cx){JSCompartment*debuggee=cx->compartment();for(EnterDebuggeeNoExecute*it=cx->noExecuteDebuggerTop;it;it=it->prev_){Debugger&dbg=it->debugger();if(!it->unlocked_&&dbg.isEnabled()&&dbg.observesGlobal(debuggee->maybeGlobal()))returnit;}returnnullptr;}// Given a JSContext entered into a debuggee compartment, report a// warning or an error if there is a lock that locks it.staticboolreportIfFoundInStack(JSContext*cx,HandleScriptscript){if(EnterDebuggeeNoExecute*nx=findInStack(cx)){boolwarning=!cx->options().throwOnDebuggeeWouldRun();if(!warning||!nx->reported_){AutoCompartmentac(cx,nx->debugger().toJSObject());nx->reported_=true;if(cx->options().dumpStackOnDebuggeeWouldRun()){fprintf(stdout,"Dumping stack for DebuggeeWouldRun:\n");DumpBacktrace(cx);}constchar*filename=script->filename()?script->filename():"(none)";charlinenoStr[15];SprintfLiteral(linenoStr,"%"PRIuSIZE,script->lineno());unsignedflags=warning?JSREPORT_WARNING:JSREPORT_ERROR;// FIXME: filename should be UTF-8 (bug 987069).returnJS_ReportErrorFlagsAndNumberLatin1(cx,flags,GetErrorMessage,nullptr,JSMSG_DEBUGGEE_WOULD_RUN,filename,linenoStr);}}returntrue;}};// Given a JSContext entered into a debuggee compartment, if it is in// an NX section, unlock the topmost EnterDebuggeeNoExecute instance.//// Does nothing if debuggee is not in an NX section. For example, this// situation arises when invocation functions are called without entering// Debugger code, e.g., calling D.O.p.executeInGlobal or D.O.p.apply.classMOZ_RAIIjs::LeaveDebuggeeNoExecute{EnterDebuggeeNoExecute*prevLocked_;public:explicitLeaveDebuggeeNoExecute(JSContext*cx):prevLocked_(EnterDebuggeeNoExecute::findInStack(cx)){if(prevLocked_){MOZ_ASSERT(!prevLocked_->unlocked_);prevLocked_->unlocked_=this;}}~LeaveDebuggeeNoExecute(){if(prevLocked_){MOZ_ASSERT(prevLocked_->unlocked_==this);prevLocked_->unlocked_=nullptr;}}};/* static */boolDebugger::slowPathCheckNoExecute(JSContext*cx,HandleScriptscript){MOZ_ASSERT(cx->compartment()->isDebuggee());MOZ_ASSERT(cx->noExecuteDebuggerTop);returnEnterDebuggeeNoExecute::reportIfFoundInStack(cx,script);}staticinlinevoidNukeDebuggerWrapper(NativeObject*wrapper){// In some OOM failure cases, we need to destroy the edge to the referent,// to avoid trying to trace it during untimely collections.wrapper->setPrivate(nullptr);}staticboolValueToStableChars(JSContext*cx,constchar*fnname,HandleValuevalue,AutoStableStringChars&stableChars){if(!value.isString()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_EXPECTED_TYPE,fnname,"string",InformalValueTypeName(value));returnfalse;}RootedLinearStringlinear(cx,value.toString()->ensureLinear(cx));if(!linear)returnfalse;if(!stableChars.initTwoByte(cx,linear))returnfalse;returntrue;}EvalOptions::~EvalOptions(){js_free(const_cast<char*>(filename_));}boolEvalOptions::setFilename(JSContext*cx,constchar*filename){char*copy=nullptr;if(filename){copy=JS_strdup(cx,filename);if(!copy)returnfalse;}// EvalOptions always owns filename_, so this cast is okay.js_free(const_cast<char*>(filename_));filename_=copy;returntrue;}staticboolParseEvalOptions(JSContext*cx,HandleValuevalue,EvalOptions&options){if(!value.isObject())returntrue;RootedObjectopts(cx,&value.toObject());RootedValuev(cx);if(!JS_GetProperty(cx,opts,"url",&v))returnfalse;if(!v.isUndefined()){RootedStringurl_str(cx,ToString<CanGC>(cx,v));if(!url_str)returnfalse;JSAutoByteStringurl_bytes(cx,url_str);if(!url_bytes)returnfalse;if(!options.setFilename(cx,url_bytes.ptr()))returnfalse;}if(!JS_GetProperty(cx,opts,"lineNumber",&v))returnfalse;if(!v.isUndefined()){uint32_tlineno;if(!ToUint32(cx,v,&lineno))returnfalse;options.setLineno(lineno);}returntrue;}staticboolRequireGlobalObject(JSContext*cx,HandleValuedbgobj,HandleObjectreferent){RootedObjectobj(cx,referent);if(!obj->is<GlobalObject>()){constchar*isWrapper="";constchar*isWindowProxy="";/* Help the poor programmer by pointing out wrappers around globals... */if(obj->is<WrapperObject>()){obj=js::UncheckedUnwrap(obj);isWrapper="a wrapper around ";}/* ... and WindowProxies around Windows. */if(IsWindowProxy(obj)){obj=ToWindowIfWindowProxy(obj);isWindowProxy="a WindowProxy referring to ";}if(obj->is<GlobalObject>()){ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_WRAPPER_IN_WAY,JSDVG_SEARCH_STACK,dbgobj,nullptr,isWrapper,isWindowProxy);}else{ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_BAD_REFERENT,JSDVG_SEARCH_STACK,dbgobj,nullptr,"a global object",nullptr);}returnfalse;}returntrue;}/*** Breakpoints *********************************************************************************/BreakpointSite::BreakpointSite(Typetype):type_(type),enabledCount(0){}voidBreakpointSite::inc(FreeOp*fop){enabledCount++;if(enabledCount==1)recompile(fop);}voidBreakpointSite::dec(FreeOp*fop){MOZ_ASSERT(enabledCount>0);enabledCount--;if(enabledCount==0)recompile(fop);}boolBreakpointSite::isEmpty()const{returnbreakpoints.isEmpty();}Breakpoint*BreakpointSite::firstBreakpoint()const{if(isEmpty())returnnullptr;return&(*breakpoints.begin());}boolBreakpointSite::hasBreakpoint(Breakpoint*toFind){constBreakpointList::Iteratorbp(toFind);for(autop=breakpoints.begin();p;p++)if(p==bp)returntrue;returnfalse;}Breakpoint::Breakpoint(Debugger*debugger,BreakpointSite*site,JSObject*handler):debugger(debugger),site(site),handler(handler){MOZ_ASSERT(handler->compartment()==debugger->object->compartment());debugger->breakpoints.pushBack(this);site->breakpoints.pushBack(this);}voidBreakpoint::destroy(FreeOp*fop){if(debugger->enabled)site->dec(fop);debugger->breakpoints.remove(this);site->breakpoints.remove(this);site->destroyIfEmpty(fop);fop->delete_(this);}Breakpoint*Breakpoint::nextInDebugger(){returndebuggerLink.mNext;}Breakpoint*Breakpoint::nextInSite(){returnsiteLink.mNext;}JSBreakpointSite::JSBreakpointSite(JSScript*script,jsbytecode*pc):BreakpointSite(Type::JS),script(script),pc(pc){MOZ_ASSERT(!script->hasBreakpointsAt(pc));}voidJSBreakpointSite::recompile(FreeOp*fop){if(script->hasBaselineScript())script->baselineScript()->toggleDebugTraps(script,pc);}voidJSBreakpointSite::destroyIfEmpty(FreeOp*fop){if(isEmpty())script->destroyBreakpointSite(fop,pc);}WasmBreakpointSite::WasmBreakpointSite(wasm::DebugState*debug_,uint32_toffset_):BreakpointSite(Type::Wasm),debug(debug_),offset(offset_){MOZ_ASSERT(debug_);}voidWasmBreakpointSite::recompile(FreeOp*fop){debug->toggleBreakpointTrap(fop->runtime(),offset,isEnabled());}voidWasmBreakpointSite::destroyIfEmpty(FreeOp*fop){if(isEmpty())debug->destroyBreakpointSite(fop,offset);}/*** Debugger hook dispatch **********************************************************************/Debugger::Debugger(JSContext*cx,NativeObject*dbg):object(dbg),debuggees(cx->runtime()),uncaughtExceptionHook(nullptr),enabled(true),allowUnobservedAsmJS(false),allowWasmBinarySource(false),collectCoverageInfo(false),observedGCs(cx->runtime()),allocationsLog(cx),trackingAllocationSites(false),allocationSamplingProbability(1.0),maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),allocationsLogOverflowed(false),frames(cx->runtime()),scripts(cx),sources(cx),objects(cx),environments(cx),wasmInstanceScripts(cx),wasmInstanceSources(cx),#ifdef NIGHTLY_BUILDtraceLoggerLastDrainedSize(0),traceLoggerLastDrainedIteration(0),#endiftraceLoggerScriptedCallsLastDrainedSize(0),traceLoggerScriptedCallsLastDrainedIteration(0){assertSameCompartment(cx,dbg);#ifdef JS_TRACE_LOGGINGTraceLoggerThread*logger=TraceLoggerForCurrentThread(cx);if(logger){#ifdef NIGHTLY_BUILDlogger->getIterationAndSize(&traceLoggerLastDrainedIteration,&traceLoggerLastDrainedSize);#endiflogger->getIterationAndSize(&traceLoggerScriptedCallsLastDrainedIteration,&traceLoggerScriptedCallsLastDrainedSize);}#endif}Debugger::~Debugger(){MOZ_ASSERT_IF(debuggees.initialized(),debuggees.empty());allocationsLog.clear();/* * We don't have to worry about locking here since Debugger is not * background finalized. */JSContext*cx=TlsContext.get();if(onNewGlobalObjectWatchersLink.mPrev||onNewGlobalObjectWatchersLink.mNext||cx->runtime()->onNewGlobalObjectWatchers().begin()==JSRuntime::WatchersList::Iterator(this))cx->runtime()->onNewGlobalObjectWatchers().remove(this);cx->runtime()->endSingleThreadedExecution(cx);}boolDebugger::init(JSContext*cx){if(!debuggees.init()||!debuggeeZones.init()||!frames.init()||!scripts.init()||!sources.init()||!objects.init()||!observedGCs.init()||!environments.init()||!wasmInstanceScripts.init()||!wasmInstanceSources.init()){ReportOutOfMemory(cx);returnfalse;}cx->zone()->group()->debuggerList().insertBack(this);returntrue;}JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER)==unsigned(JSSLOT_DEBUGSCRIPT_OWNER));JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER)==unsigned(JSSLOT_DEBUGSOURCE_OWNER));JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER)==unsigned(JSSLOT_DEBUGOBJECT_OWNER));JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER)==unsigned(DebuggerEnvironment::OWNER_SLOT));/* static */Debugger*Debugger::fromChildJSObject(JSObject*obj){MOZ_ASSERT(obj->getClass()==&DebuggerFrame::class_||obj->getClass()==&DebuggerScript_class||obj->getClass()==&DebuggerSource_class||obj->getClass()==&DebuggerObject::class_||obj->getClass()==&DebuggerEnvironment::class_);JSObject*dbgobj=&obj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER).toObject();returnfromJSObject(dbgobj);}boolDebugger::hasMemory()const{returnobject->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).isObject();}DebuggerMemory&Debugger::memory()const{MOZ_ASSERT(hasMemory());returnobject->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).toObject().as<DebuggerMemory>();}boolDebugger::getScriptFrameWithIter(JSContext*cx,AbstractFramePtrreferent,constFrameIter*maybeIter,MutableHandleValuevp){RootedDebuggerFrameresult(cx);if(!Debugger::getScriptFrameWithIter(cx,referent,maybeIter,&result))returnfalse;vp.setObject(*result);returntrue;}boolDebugger::getScriptFrameWithIter(JSContext*cx,AbstractFramePtrreferent,constFrameIter*maybeIter,MutableHandleDebuggerFrameresult){MOZ_ASSERT_IF(maybeIter,maybeIter->abstractFramePtr()==referent);MOZ_ASSERT_IF(referent.hasScript(),!referent.script()->selfHosted());if(referent.hasScript()&&!referent.script()->ensureHasAnalyzedArgsUsage(cx))returnfalse;FrameMap::AddPtrp=frames.lookupForAdd(referent);if(!p){/* Create and populate the Debugger.Frame object. */RootedObjectproto(cx,&object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());RootedNativeObjectdebugger(cx,object);RootedDebuggerFrameframe(cx,DebuggerFrame::create(cx,proto,referent,maybeIter,debugger));if(!frame)returnfalse;if(!ensureExecutionObservabilityOfFrame(cx,referent))returnfalse;if(!frames.add(p,referent,frame)){ReportOutOfMemory(cx);returnfalse;}}result.set(&p->value()->as<DebuggerFrame>());returntrue;}/* static */boolDebugger::hasLiveHook(GlobalObject*global,Hookwhich){if(GlobalObject::DebuggerVector*debuggers=global->getDebuggers()){for(autop=debuggers->begin();p!=debuggers->end();p++){Debugger*dbg=*p;if(dbg->enabled&&dbg->getHook(which))returntrue;}}returnfalse;}JSObject*Debugger::getHook(Hookhook)const{MOZ_ASSERT(hook>=0&&hook<HookCount);constValue&v=object->getReservedSlot(JSSLOT_DEBUG_HOOK_START+hook);returnv.isUndefined()?nullptr:&v.toObject();}boolDebugger::hasAnyLiveHooks(JSRuntime*rt)const{if(!enabled)returnfalse;if(getHook(OnDebuggerStatement)||getHook(OnExceptionUnwind)||getHook(OnNewScript)||getHook(OnEnterFrame)){returntrue;}/* If any breakpoints are in live scripts, return true. */for(Breakpoint*bp=firstBreakpoint();bp;bp=bp->nextInDebugger()){switch(bp->site->type()){caseBreakpointSite::Type::JS:if(IsMarkedUnbarriered(rt,&bp->site->asJS()->script))returntrue;break;caseBreakpointSite::Type::Wasm:if(IsMarkedUnbarriered(rt,&bp->asWasm()->wasmInstance))returntrue;break;}}for(FrameMap::Ranger=frames.all();!r.empty();r.popFront()){NativeObject*frameObj=r.front().value();if(!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined()||!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())returntrue;}returnfalse;}/* static */JSTrapStatusDebugger::slowPathOnEnterFrame(JSContext*cx,AbstractFramePtrframe){RootedValuerval(cx);JSTrapStatusstatus=dispatchHook(cx,[frame](Debugger*dbg)->bool{returndbg->observesFrame(frame)&&dbg->observesEnterFrame();},[&](Debugger*dbg)->JSTrapStatus{returndbg->fireEnterFrame(cx,&rval);});switch(status){caseJSTRAP_CONTINUE:break;caseJSTRAP_THROW:cx->setPendingException(rval);break;caseJSTRAP_ERROR:cx->clearPendingException();break;caseJSTRAP_RETURN:frame.setReturnValue(rval);break;default:MOZ_CRASH("bad Debugger::onEnterFrame JSTrapStatus value");}returnstatus;}staticvoidDebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp*fop,AbstractFramePtrframe,NativeObject*frameobj);staticvoidDebuggerFrame_freeScriptFrameIterData(FreeOp*fop,JSObject*obj);/* * Handle leaving a frame with debuggers watching. |frameOk| indicates whether * the frame is exiting normally or abruptly. Set |cx|'s exception and/or * |cx->fp()|'s return value, and return a new success value. *//* static */boolDebugger::slowPathOnLeaveFrame(JSContext*cx,AbstractFramePtrframe,jsbytecode*pc,boolframeOk){mozilla::DebugOnly<Handle<GlobalObject*>>debuggeeGlobal=cx->global();autoframeMapsGuard=MakeScopeExit([&]{// Clean up all Debugger.Frame instances.removeFromFrameMapsAndClearBreakpointsIn(cx,frame);});// The onPop handler and associated clean up logic should not run multiple// times on the same frame. If slowPathOnLeaveFrame has already been// called, the frame will not be present in the Debugger frame maps.Rooted<DebuggerFrameVector>frames(cx,DebuggerFrameVector(cx));if(!getDebuggerFrames(frame,&frames))returnfalse;if(frames.empty())returnframeOk;/* Save the frame's completion value. */JSTrapStatusstatus;RootedValuevalue(cx);Debugger::resultToCompletion(cx,frameOk,frame.returnValue(),&status,&value);// This path can be hit via unwinding the stack due to over-recursion or// OOM. In those cases, don't fire the frames' onPop handlers, because// invoking JS will only trigger the same condition. See// slowPathOnExceptionUnwind.if(!cx->isThrowingOverRecursed()&&!cx->isThrowingOutOfMemory()){/* For each Debugger.Frame, fire its onPop handler, if any. */for(size_ti=0;i<frames.length();i++){HandleDebuggerFrameframeobj=frames[i];Debugger*dbg=Debugger::fromChildJSObject(frameobj);EnterDebuggeeNoExecutenx(cx,*dbg);if(dbg->enabled&&frameobj->onPopHandler()){OnPopHandler*handler=frameobj->onPopHandler();Maybe<AutoCompartment>ac;ac.emplace(cx,dbg->object);RootedValuewrappedValue(cx,value);RootedValuecompletion(cx);if(!dbg->wrapDebuggeeValue(cx,&wrappedValue)){status=dbg->reportUncaughtException(ac);break;}/* Call the onPop handler. */JSTrapStatusnextStatus=status;RootedValuenextValue(cx,wrappedValue);boolsuccess=handler->onPop(cx,frameobj,nextStatus,&nextValue);nextStatus=dbg->processParsedHandlerResult(ac,frame,pc,success,nextStatus,&nextValue);/* * At this point, we are back in the debuggee compartment, and any error has * been wrapped up as a completion value. */MOZ_ASSERT(cx->compartment()==debuggeeGlobal->compartment());MOZ_ASSERT(!cx->isExceptionPending());/* JSTRAP_CONTINUE means "make no change". */if(nextStatus!=JSTRAP_CONTINUE){status=nextStatus;value=nextValue;}}}}/* Establish (status, value) as our resumption value. */switch(status){caseJSTRAP_RETURN:frame.setReturnValue(value);returntrue;caseJSTRAP_THROW:cx->setPendingException(value);returnfalse;caseJSTRAP_ERROR:MOZ_ASSERT(!cx->isExceptionPending());returnfalse;default:MOZ_CRASH("bad final trap status");}}/* static */JSTrapStatusDebugger::slowPathOnDebuggerStatement(JSContext*cx,AbstractFramePtrframe){RootedValuerval(cx);JSTrapStatusstatus=dispatchHook(cx,[](Debugger*dbg)->bool{returndbg->getHook(OnDebuggerStatement);},[&](Debugger*dbg)->JSTrapStatus{returndbg->fireDebuggerStatement(cx,&rval);});switch(status){caseJSTRAP_CONTINUE:caseJSTRAP_ERROR:break;caseJSTRAP_RETURN:frame.setReturnValue(rval);break;caseJSTRAP_THROW:cx->setPendingException(rval);break;default:MOZ_CRASH("Invalid onDebuggerStatement trap status");}returnstatus;}/* static */JSTrapStatusDebugger::slowPathOnExceptionUnwind(JSContext*cx,AbstractFramePtrframe){// Invoking more JS on an over-recursed stack or after OOM is only going// to result in more of the same error.if(cx->isThrowingOverRecursed()||cx->isThrowingOutOfMemory())returnJSTRAP_CONTINUE;// The Debugger API mustn't muck with frames from self-hosted scripts.if(frame.hasScript()&&frame.script()->selfHosted())returnJSTRAP_CONTINUE;RootedValuerval(cx);JSTrapStatusstatus=dispatchHook(cx,[](Debugger*dbg)->bool{returndbg->getHook(OnExceptionUnwind);},[&](Debugger*dbg)->JSTrapStatus{returndbg->fireExceptionUnwind(cx,&rval);});switch(status){caseJSTRAP_CONTINUE:break;caseJSTRAP_THROW:cx->setPendingException(rval);break;caseJSTRAP_ERROR:cx->clearPendingException();break;caseJSTRAP_RETURN:cx->clearPendingException();frame.setReturnValue(rval);break;default:MOZ_CRASH("Invalid onExceptionUnwind trap status");}returnstatus;}// TODO: Remove Remove this function when all properties/methods returning a/// DebuggerEnvironment have been given a C++ interface (bug 1271649).boolDebugger::wrapEnvironment(JSContext*cx,Handle<Env*>env,MutableHandleValuerval){if(!env){rval.setNull();returntrue;}RootedDebuggerEnvironmentenvobj(cx);if(!wrapEnvironment(cx,env,&envobj))returnfalse;rval.setObject(*envobj);returntrue;}boolDebugger::wrapEnvironment(JSContext*cx,Handle<Env*>env,MutableHandleDebuggerEnvironmentresult){MOZ_ASSERT(env);/* * DebuggerEnv should only wrap a debug scope chain obtained (transitively) * from GetDebugEnvironmentFor(Frame|Function). */MOZ_ASSERT(!IsSyntacticEnvironment(env));DependentAddPtr<ObjectWeakMap>p(cx,environments,env);if(p){result.set(&p->value()->as<DebuggerEnvironment>());}else{/* Create a new Debugger.Environment for env. */RootedObjectproto(cx,&object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject());RootedNativeObjectdebugger(cx,object);RootedDebuggerEnvironmentenvobj(cx,DebuggerEnvironment::create(cx,proto,env,debugger));if(!envobj)returnfalse;if(!p.add(cx,environments,env,envobj)){NukeDebuggerWrapper(envobj);returnfalse;}CrossCompartmentKeykey(object,env,CrossCompartmentKey::DebuggerEnvironment);if(!object->compartment()->putWrapper(cx,key,ObjectValue(*envobj))){NukeDebuggerWrapper(envobj);environments.remove(env);returnfalse;}result.set(envobj);}returntrue;}boolDebugger::wrapDebuggeeValue(JSContext*cx,MutableHandleValuevp){assertSameCompartment(cx,object.get());if(vp.isObject()){RootedObjectobj(cx,&vp.toObject());RootedDebuggerObjectdobj(cx);if(!wrapDebuggeeObject(cx,obj,&dobj))returnfalse;vp.setObject(*dobj);}elseif(vp.isMagic()){RootedPlainObjectoptObj(cx,NewBuiltinClassInstance<PlainObject>(cx));if(!optObj)returnfalse;// We handle three sentinel values: missing arguments (overloading// JS_OPTIMIZED_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),// and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).//// Other magic values should not have escaped.PropertyName*name;switch(vp.whyMagic()){caseJS_OPTIMIZED_ARGUMENTS:name=cx->names().missingArguments;break;caseJS_OPTIMIZED_OUT:name=cx->names().optimizedOut;break;caseJS_UNINITIALIZED_LEXICAL:name=cx->names().uninitialized;break;default:MOZ_CRASH("Unsupported magic value escaped to Debugger");}RootedValuetrueVal(cx,BooleanValue(true));if(!DefineProperty(cx,optObj,name,trueVal))returnfalse;vp.setObject(*optObj);}elseif(!cx->compartment()->wrap(cx,vp)){vp.setUndefined();returnfalse;}returntrue;}boolDebugger::wrapDebuggeeObject(JSContext*cx,HandleObjectobj,MutableHandleDebuggerObjectresult){MOZ_ASSERT(obj);if(obj->is<JSFunction>()){MOZ_ASSERT(!IsInternalFunctionObject(*obj));RootedFunctionfun(cx,&obj->as<JSFunction>());if(!EnsureFunctionHasScript(cx,fun))returnfalse;}DependentAddPtr<ObjectWeakMap>p(cx,objects,obj);if(p){result.set(&p->value()->as<DebuggerObject>());}else{/* Create a new Debugger.Object for obj. */RootedNativeObjectdebugger(cx,object);RootedObjectproto(cx,&object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());RootedDebuggerObjectdobj(cx,DebuggerObject::create(cx,proto,obj,debugger));if(!dobj)returnfalse;if(!p.add(cx,objects,obj,dobj)){NukeDebuggerWrapper(dobj);returnfalse;}if(obj->compartment()!=object->compartment()){CrossCompartmentKeykey(object,obj,CrossCompartmentKey::DebuggerObject);if(!object->compartment()->putWrapper(cx,key,ObjectValue(*dobj))){NukeDebuggerWrapper(dobj);objects.remove(obj);ReportOutOfMemory(cx);returnfalse;}}result.set(dobj);}returntrue;}staticNativeObject*ToNativeDebuggerObject(JSContext*cx,MutableHandleObjectobj){if(obj->getClass()!=&DebuggerObject::class_){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_EXPECTED_TYPE,"Debugger","Debugger.Object",obj->getClass()->name);returnnullptr;}NativeObject*ndobj=&obj->as<NativeObject>();Valueowner=ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);if(owner.isUndefined()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_PROTO,"Debugger.Object","Debugger.Object");returnnullptr;}returnndobj;}boolDebugger::unwrapDebuggeeObject(JSContext*cx,MutableHandleObjectobj){NativeObject*ndobj=ToNativeDebuggerObject(cx,obj);if(!ndobj)returnfalse;Valueowner=ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);if(&owner.toObject()!=object){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_WRONG_OWNER,"Debugger.Object");returnfalse;}obj.set(static_cast<JSObject*>(ndobj->getPrivate()));returntrue;}boolDebugger::unwrapDebuggeeValue(JSContext*cx,MutableHandleValuevp){assertSameCompartment(cx,object.get(),vp);if(vp.isObject()){RootedObjectdobj(cx,&vp.toObject());if(!unwrapDebuggeeObject(cx,&dobj))returnfalse;vp.setObject(*dobj);}returntrue;}staticboolCheckArgCompartment(JSContext*cx,JSObject*obj,JSObject*arg,constchar*methodname,constchar*propname){if(arg->compartment()!=obj->compartment()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_COMPARTMENT_MISMATCH,methodname,propname);returnfalse;}returntrue;}staticboolCheckArgCompartment(JSContext*cx,JSObject*obj,HandleValuev,constchar*methodname,constchar*propname){if(v.isObject())returnCheckArgCompartment(cx,obj,&v.toObject(),methodname,propname);returntrue;}boolDebugger::unwrapPropertyDescriptor(JSContext*cx,HandleObjectobj,MutableHandle<PropertyDescriptor>desc){if(desc.hasValue()){RootedValuevalue(cx,desc.value());if(!unwrapDebuggeeValue(cx,&value)||!CheckArgCompartment(cx,obj,value,"defineProperty","value")){returnfalse;}desc.setValue(value);}if(desc.hasGetterObject()){RootedObjectget(cx,desc.getterObject());if(get){if(!unwrapDebuggeeObject(cx,&get))returnfalse;if(!CheckArgCompartment(cx,obj,get,"defineProperty","get"))returnfalse;}desc.setGetterObject(get);}if(desc.hasSetterObject()){RootedObjectset(cx,desc.setterObject());if(set){if(!unwrapDebuggeeObject(cx,&set))returnfalse;if(!CheckArgCompartment(cx,obj,set,"defineProperty","set"))returnfalse;}desc.setSetterObject(set);}returntrue;}JSTrapStatusDebugger::reportUncaughtException(Maybe<AutoCompartment>&ac){JSContext*cx=ac->context();// Uncaught exceptions arise from Debugger code, and so we must already be// in an NX section.MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx,*this));if(cx->isExceptionPending()){/* * We want to report the pending exception, but we want to let the * embedding handle it however it wants to. So pretend like we're * starting a new script execution on our current compartment (which * is the debugger compartment, so reported errors won't get * reported to various onerror handlers in debuggees) and as part of * that "execution" simply throw our exception so the embedding can * deal. */RootedValueexn(cx);if(cx->getPendingException(&exn)){/* * Clear the exception, because ReportErrorToGlobal will assert that * we don't have one. */cx->clearPendingException();ReportErrorToGlobal(cx,cx->global(),exn);}/* * And if not, or if PrepareScriptEnvironmentAndInvoke somehow left * an exception on cx (which it totally shouldn't do), just give * up. */cx->clearPendingException();}ac.reset();returnJSTRAP_ERROR;}JSTrapStatusDebugger::handleUncaughtExceptionHelper(Maybe<AutoCompartment>&ac,MutableHandleValue*vp,constMaybe<HandleValue>&thisVForCheck,AbstractFramePtrframe){JSContext*cx=ac->context();// Uncaught exceptions arise from Debugger code, and so we must already be// in an NX section.MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx,*this));if(cx->isExceptionPending()){if(uncaughtExceptionHook){RootedValueexc(cx);if(!cx->getPendingException(&exc))returnJSTRAP_ERROR;cx->clearPendingException();RootedValuefval(cx,ObjectValue(*uncaughtExceptionHook));RootedValuerv(cx);if(js::Call(cx,fval,object,exc,&rv)){if(vp){JSTrapStatusstatus=JSTRAP_CONTINUE;if(processResumptionValue(ac,frame,thisVForCheck,rv,status,*vp))returnstatus;}else{returnJSTRAP_CONTINUE;}}}returnreportUncaughtException(ac);}ac.reset();returnJSTRAP_ERROR;}JSTrapStatusDebugger::handleUncaughtException(Maybe<AutoCompartment>&ac,MutableHandleValuevp,constMaybe<HandleValue>&thisVForCheck,AbstractFramePtrframe){returnhandleUncaughtExceptionHelper(ac,&vp,thisVForCheck,frame);}JSTrapStatusDebugger::handleUncaughtException(Maybe<AutoCompartment>&ac){returnhandleUncaughtExceptionHelper(ac,nullptr,mozilla::Nothing(),NullFramePtr());}/* static */voidDebugger::resultToCompletion(JSContext*cx,boolok,constValue&rv,JSTrapStatus*status,MutableHandleValuevalue){MOZ_ASSERT_IF(ok,!cx->isExceptionPending());if(ok){*status=JSTRAP_RETURN;value.set(rv);}elseif(cx->isExceptionPending()){*status=JSTRAP_THROW;if(!cx->getPendingException(value))*status=JSTRAP_ERROR;cx->clearPendingException();}else{*status=JSTRAP_ERROR;value.setUndefined();}}boolDebugger::newCompletionValue(JSContext*cx,JSTrapStatusstatus,constValue&value_,MutableHandleValueresult){/* * We must be in the debugger's compartment, since that's where we want * to construct the completion value. */assertSameCompartment(cx,object.get());assertSameCompartment(cx,value_);RootedIdkey(cx);RootedValuevalue(cx,value_);switch(status){caseJSTRAP_RETURN:key=NameToId(cx->names().return_);break;caseJSTRAP_THROW:key=NameToId(cx->names().throw_);break;caseJSTRAP_ERROR:result.setNull();returntrue;default:MOZ_CRASH("bad status passed to Debugger::newCompletionValue");}/* Common tail for JSTRAP_RETURN and JSTRAP_THROW. */RootedPlainObjectobj(cx,NewBuiltinClassInstance<PlainObject>(cx));if(!obj||!NativeDefineProperty(cx,obj,key,value,nullptr,nullptr,JSPROP_ENUMERATE)){returnfalse;}result.setObject(*obj);returntrue;}boolDebugger::receiveCompletionValue(Maybe<AutoCompartment>&ac,boolok,HandleValueval,MutableHandleValuevp){JSContext*cx=ac->context();JSTrapStatusstatus;RootedValuevalue(cx);resultToCompletion(cx,ok,val,&status,&value);ac.reset();returnwrapDebuggeeValue(cx,&value)&&newCompletionValue(cx,status,value,vp);}staticboolGetStatusProperty(JSContext*cx,HandleObjectobj,HandlePropertyNamename,JSTrapStatusstatus,JSTrapStatus&statusp,MutableHandleValuevp,int*hits){boolfound;if(!HasProperty(cx,obj,name,&found))returnfalse;if(found){++*hits;statusp=status;if(!GetProperty(cx,obj,obj,name,vp))returnfalse;}returntrue;}staticboolParseResumptionValueAsObject(JSContext*cx,HandleValuerv,JSTrapStatus&statusp,MutableHandleValuevp){inthits=0;if(rv.isObject()){RootedObjectobj(cx,&rv.toObject());if(!GetStatusProperty(cx,obj,cx->names().return_,JSTRAP_RETURN,statusp,vp,&hits))returnfalse;if(!GetStatusProperty(cx,obj,cx->names().throw_,JSTRAP_THROW,statusp,vp,&hits))returnfalse;}if(hits!=1){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_RESUMPTION);returnfalse;}returntrue;}staticboolParseResumptionValue(JSContext*cx,HandleValuerval,JSTrapStatus&statusp,MutableHandleValuevp){if(rval.isUndefined()){statusp=JSTRAP_CONTINUE;vp.setUndefined();returntrue;}if(rval.isNull()){statusp=JSTRAP_ERROR;vp.setUndefined();returntrue;}returnParseResumptionValueAsObject(cx,rval,statusp,vp);}staticboolCheckResumptionValue(JSContext*cx,AbstractFramePtrframe,constMaybe<HandleValue>&maybeThisv,JSTrapStatusstatus,MutableHandleValuevp){if(status==JSTRAP_RETURN&&frame&&frame.isFunctionFrame()){// Don't let a { return: ... } resumption value make a generator// function violate the iterator protocol. The return value from// such a frame must have the form { done: <bool>, value: <anything> }.RootedFunctioncallee(cx,frame.callee());if(callee->isStarGenerator()){if(!CheckStarGeneratorResumptionValue(cx,vp)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_YIELD);returnfalse;}}}if(maybeThisv.isSome()){constHandleValue&thisv=maybeThisv.ref();if(status==JSTRAP_RETURN&&vp.isPrimitive()){if(vp.isUndefined()){if(thisv.isMagic(JS_UNINITIALIZED_LEXICAL))returnThrowUninitializedThis(cx,frame);vp.set(thisv);}else{ReportValueError(cx,JSMSG_BAD_DERIVED_RETURN,JSDVG_IGNORE_STACK,vp,nullptr);returnfalse;}}}returntrue;}staticboolGetThisValueForCheck(JSContext*cx,AbstractFramePtrframe,jsbytecode*pc,MutableHandleValuethisv,Maybe<HandleValue>&maybeThisv){if(frame.debuggerNeedsCheckPrimitiveReturn()){{AutoCompartmentac(cx,frame.environmentChain());if(!GetThisValueForDebuggerMaybeOptimizedOut(cx,frame,pc,thisv))returnfalse;}if(!cx->compartment()->wrap(cx,thisv))returnfalse;MOZ_ASSERT_IF(thisv.isMagic(),thisv.isMagic(JS_UNINITIALIZED_LEXICAL));maybeThisv.emplace(HandleValue(thisv));}returntrue;}boolDebugger::processResumptionValue(Maybe<AutoCompartment>&ac,AbstractFramePtrframe,constMaybe<HandleValue>&maybeThisv,HandleValuerval,JSTrapStatus&statusp,MutableHandleValuevp){JSContext*cx=ac->context();if(!ParseResumptionValue(cx,rval,statusp,vp)||!unwrapDebuggeeValue(cx,vp)||!CheckResumptionValue(cx,frame,maybeThisv,statusp,vp)){returnfalse;}ac.reset();if(!cx->compartment()->wrap(cx,vp)){statusp=JSTRAP_ERROR;vp.setUndefined();}returntrue;}JSTrapStatusDebugger::processParsedHandlerResultHelper(Maybe<AutoCompartment>&ac,AbstractFramePtrframe,constMaybe<HandleValue>&maybeThisv,boolsuccess,JSTrapStatusstatus,MutableHandleValuevp){if(!success)returnhandleUncaughtException(ac,vp,maybeThisv,frame);JSContext*cx=ac->context();if(!unwrapDebuggeeValue(cx,vp)||!CheckResumptionValue(cx,frame,maybeThisv,status,vp)){returnhandleUncaughtException(ac,vp,maybeThisv,frame);}ac.reset();if(!cx->compartment()->wrap(cx,vp)){status=JSTRAP_ERROR;vp.setUndefined();}returnstatus;}JSTrapStatusDebugger::processParsedHandlerResult(Maybe<AutoCompartment>&ac,AbstractFramePtrframe,jsbytecode*pc,boolsuccess,JSTrapStatusstatus,MutableHandleValuevp){JSContext*cx=ac->context();RootedValuethisv(cx);Maybe<HandleValue>maybeThisv;if(!GetThisValueForCheck(cx,frame,pc,&thisv,maybeThisv)){ac.reset();returnJSTRAP_ERROR;}returnprocessParsedHandlerResultHelper(ac,frame,maybeThisv,success,status,vp);}JSTrapStatusDebugger::processHandlerResult(Maybe<AutoCompartment>&ac,boolsuccess,constValue&rv,AbstractFramePtrframe,jsbytecode*pc,MutableHandleValuevp){JSContext*cx=ac->context();RootedValuethisv(cx);Maybe<HandleValue>maybeThisv;if(!GetThisValueForCheck(cx,frame,pc,&thisv,maybeThisv)){ac.reset();returnJSTRAP_ERROR;}if(!success)returnhandleUncaughtException(ac,vp,maybeThisv,frame);RootedValuerootRv(cx,rv);JSTrapStatusstatus=JSTRAP_CONTINUE;success=ParseResumptionValue(cx,rootRv,status,vp);returnprocessParsedHandlerResultHelper(ac,frame,maybeThisv,success,status,vp);}staticboolCallMethodIfPresent(JSContext*cx,HandleObjectobj,constchar*name,size_targc,Value*argv,MutableHandleValuerval){rval.setUndefined();JSAtom*atom=Atomize(cx,name,strlen(name));if(!atom)returnfalse;RootedIdid(cx,AtomToId(atom));RootedValuefval(cx);if(!GetProperty(cx,obj,obj,id,&fval))returnfalse;if(!IsCallable(fval))returntrue;InvokeArgsargs(cx);if(!args.init(cx,argc))returnfalse;for(size_ti=0;i<argc;i++)args[i].set(argv[i]);rval.setObject(*obj);// overwritten by successful Callreturnjs::Call(cx,fval,rval,args,rval);}JSTrapStatusDebugger::fireDebuggerStatement(JSContext*cx,MutableHandleValuevp){RootedObjecthook(cx,getHook(OnDebuggerStatement));MOZ_ASSERT(hook);MOZ_ASSERT(hook->isCallable());Maybe<AutoCompartment>ac;ac.emplace(cx,object);ScriptFrameIteriter(cx);RootedValuescriptFrame(cx);if(!getScriptFrame(cx,iter,&scriptFrame))returnreportUncaughtException(ac);RootedValuefval(cx,ObjectValue(*hook));RootedValuerv(cx);boolok=js::Call(cx,fval,object,scriptFrame,&rv);returnprocessHandlerResult(ac,ok,rv,iter.abstractFramePtr(),iter.pc(),vp);}JSTrapStatusDebugger::fireExceptionUnwind(JSContext*cx,MutableHandleValuevp){RootedObjecthook(cx,getHook(OnExceptionUnwind));MOZ_ASSERT(hook);MOZ_ASSERT(hook->isCallable());RootedValueexc(cx);if(!cx->getPendingException(&exc))returnJSTRAP_ERROR;cx->clearPendingException();Maybe<AutoCompartment>ac;ac.emplace(cx,object);RootedValuescriptFrame(cx);RootedValuewrappedExc(cx,exc);FrameIteriter(cx);if(!getScriptFrame(cx,iter,&scriptFrame)||!wrapDebuggeeValue(cx,&wrappedExc))returnreportUncaughtException(ac);RootedValuefval(cx,ObjectValue(*hook));RootedValuerv(cx);boolok=js::Call(cx,fval,object,scriptFrame,wrappedExc,&rv);JSTrapStatusst=processHandlerResult(ac,ok,rv,iter.abstractFramePtr(),iter.pc(),vp);if(st==JSTRAP_CONTINUE)cx->setPendingException(exc);returnst;}JSTrapStatusDebugger::fireEnterFrame(JSContext*cx,MutableHandleValuevp){RootedObjecthook(cx,getHook(OnEnterFrame));MOZ_ASSERT(hook);MOZ_ASSERT(hook->isCallable());Maybe<AutoCompartment>ac;ac.emplace(cx,object);RootedValuescriptFrame(cx);FrameIteriter(cx);if(!getScriptFrame(cx,iter,&scriptFrame))returnreportUncaughtException(ac);RootedValuefval(cx,ObjectValue(*hook));RootedValuerv(cx);boolok=js::Call(cx,fval,object,scriptFrame,&rv);returnprocessHandlerResult(ac,ok,rv,iter.abstractFramePtr(),iter.pc(),vp);}voidDebugger::fireNewScript(JSContext*cx,Handle<DebuggerScriptReferent>scriptReferent){RootedObjecthook(cx,getHook(OnNewScript));MOZ_ASSERT(hook);MOZ_ASSERT(hook->isCallable());Maybe<AutoCompartment>ac;ac.emplace(cx,object);JSObject*dsobj=wrapVariantReferent(cx,scriptReferent);if(!dsobj){reportUncaughtException(ac);return;}RootedValuefval(cx,ObjectValue(*hook));RootedValuedsval(cx,ObjectValue(*dsobj));RootedValuerv(cx);if(!js::Call(cx,fval,object,dsval,&rv))handleUncaughtException(ac);}voidDebugger::fireOnGarbageCollectionHook(JSContext*cx,constJS::dbg::GarbageCollectionEvent::Ptr&gcData){MOZ_ASSERT(observedGC(gcData->majorGCNumber()));observedGCs.remove(gcData->majorGCNumber());RootedObjecthook(cx,getHook(OnGarbageCollection));MOZ_ASSERT(hook);MOZ_ASSERT(hook->isCallable());Maybe<AutoCompartment>ac;ac.emplace(cx,object);JSObject*dataObj=gcData->toJSObject(cx);if(!dataObj){reportUncaughtException(ac);return;}RootedValuefval(cx,ObjectValue(*hook));RootedValuedataVal(cx,ObjectValue(*dataObj));RootedValuerv(cx);if(!js::Call(cx,fval,object,dataVal,&rv))handleUncaughtException(ac);}template<typenameHookIsEnabledFun/* bool (Debugger*) */,typenameFireHookFun/* JSTrapStatus (Debugger*) */>/* static */JSTrapStatusDebugger::dispatchHook(JSContext*cx,HookIsEnabledFunhookIsEnabled,FireHookFunfireHook){/* * Determine which debuggers will receive this event, and in what order. * Make a copy of the list, since the original is mutable and we will be * calling into arbitrary JS. * * Note: In the general case, 'triggered' contains references to objects in * different compartments--every compartment *except* this one. */AutoValueVectortriggered(cx);Handle<GlobalObject*>global=cx->global();if(GlobalObject::DebuggerVector*debuggers=global->getDebuggers()){for(autop=debuggers->begin();p!=debuggers->end();p++){Debugger*dbg=*p;if(dbg->enabled&&hookIsEnabled(dbg)){if(!triggered.append(ObjectValue(*dbg->toJSObject())))returnJSTRAP_ERROR;}}}/* * Deliver the event to each debugger, checking again to make sure it * should still be delivered. */for(Value*p=triggered.begin();p!=triggered.end();p++){Debugger*dbg=Debugger::fromJSObject(&p->toObject());EnterDebuggeeNoExecutenx(cx,*dbg);if(dbg->debuggees.has(global)&&dbg->enabled&&hookIsEnabled(dbg)){JSTrapStatusst=fireHook(dbg);if(st!=JSTRAP_CONTINUE)returnst;}}returnJSTRAP_CONTINUE;}voidDebugger::slowPathOnNewScript(JSContext*cx,HandleScriptscript){JSTrapStatusstatus=dispatchHook(cx,[script](Debugger*dbg)->bool{returndbg->observesNewScript()&&dbg->observesScript(script);},[&](Debugger*dbg)->JSTrapStatus{Rooted<DebuggerScriptReferent>scriptReferent(cx,script.get());dbg->fireNewScript(cx,scriptReferent);returnJSTRAP_CONTINUE;});// dispatchHook may fail due to OOM. This OOM is not handlable at the// callsites of onNewScript in the engine.if(status==JSTRAP_ERROR){cx->clearPendingException();return;}MOZ_ASSERT(status==JSTRAP_CONTINUE);}voidDebugger::slowPathOnNewWasmInstance(JSContext*cx,Handle<WasmInstanceObject*>wasmInstance){JSTrapStatusstatus=dispatchHook(cx,[wasmInstance](Debugger*dbg)->bool{returndbg->observesNewScript()&&dbg->observesGlobal(&wasmInstance->global());},[&](Debugger*dbg)->JSTrapStatus{Rooted<DebuggerScriptReferent>scriptReferent(cx,wasmInstance.get());dbg->fireNewScript(cx,scriptReferent);returnJSTRAP_CONTINUE;});// dispatchHook may fail due to OOM. This OOM is not handlable at the// callsites of onNewWasmInstance in the engine.if(status==JSTRAP_ERROR){cx->clearPendingException();return;}MOZ_ASSERT(status==JSTRAP_CONTINUE);}/* static */JSTrapStatusDebugger::onTrap(JSContext*cx,MutableHandleValuevp){FrameIteriter(cx);Rooted<GlobalObject*>global(cx);BreakpointSite*site;boolisJS;// true when iter.hasScript(), false when iter.isWasm()jsbytecode*pc;// valid when isJS == trueuint32_tbytecodeOffset;// valid when isJS == falseif(iter.hasScript()){RootedScriptscript(cx,iter.script());MOZ_ASSERT(script->isDebuggee());global.set(&script->global());isJS=true;pc=iter.pc();bytecodeOffset=0;site=script->getBreakpointSite(pc);}else{MOZ_ASSERT(iter.isWasm());global.set(&iter.wasmInstance()->object()->global());isJS=false;pc=nullptr;bytecodeOffset=iter.wasmBytecodeOffset();site=iter.wasmInstance()->debug().getOrCreateBreakpointSite(cx,bytecodeOffset);}/* Build list of breakpoint handlers. */Vector<Breakpoint*>triggered(cx);for(Breakpoint*bp=site->firstBreakpoint();bp;bp=bp->nextInSite()){// Skip a breakpoint that is not set for the current wasm::Instance --// single wasm::Code can handle breakpoints for mutiple instances.if(!isJS&&&bp->asWasm()->wasmInstance->instance()!=iter.wasmInstance())continue;if(!triggered.append(bp))returnJSTRAP_ERROR;}for(Breakpoint**p=triggered.begin();p!=triggered.end();p++){Breakpoint*bp=*p;/* Handlers can clear breakpoints. Check that bp still exists. */if(!site||!site->hasBreakpoint(bp))continue;/* * There are two reasons we have to check whether dbg is enabled and * debugging global. * * One is just that one breakpoint handler can disable other Debuggers * or remove debuggees. * * The other has to do with non-compile-and-go scripts, which have no * specific global--until they are executed. Only now do we know which * global the script is running against. */Debugger*dbg=bp->debugger;boolhasDebuggee=dbg->enabled&&dbg->debuggees.has(global);if(hasDebuggee){Maybe<AutoCompartment>ac;ac.emplace(cx,dbg->object);EnterDebuggeeNoExecutenx(cx,*dbg);RootedValuescriptFrame(cx);if(!dbg->getScriptFrame(cx,iter,&scriptFrame))returndbg->reportUncaughtException(ac);RootedValuerv(cx);Rooted<JSObject*>handler(cx,bp->handler);boolok=CallMethodIfPresent(cx,handler,"hit",1,scriptFrame.address(),&rv);JSTrapStatusst=dbg->processHandlerResult(ac,ok,rv,iter.abstractFramePtr(),iter.pc(),vp);if(st!=JSTRAP_CONTINUE)returnst;/* Calling JS code invalidates site. Reload it. */if(isJS)site=iter.script()->getBreakpointSite(pc);elsesite=iter.wasmInstance()->debug().getOrCreateBreakpointSite(cx,bytecodeOffset);}}// By convention, return the true op to the interpreter in vp, and return// undefined in vp to the wasm debug trap.if(isJS)vp.setInt32(JSOp(*pc));elsevp.set(UndefinedValue());returnJSTRAP_CONTINUE;}/* static */JSTrapStatusDebugger::onSingleStep(JSContext*cx,MutableHandleValuevp){FrameIteriter(cx);/* * We may be stepping over a JSOP_EXCEPTION, that pushes the context's * pending exception for a 'catch' clause to handle. Don't let the * onStep handlers mess with that (other than by returning a resumption * value). */RootedValueexception(cx,UndefinedValue());boolexceptionPending=cx->isExceptionPending();if(exceptionPending){if(!cx->getPendingException(&exception))returnJSTRAP_ERROR;cx->clearPendingException();}/* * Build list of Debugger.Frame instances referring to this frame with * onStep handlers. */Rooted<DebuggerFrameVector>frames(cx,DebuggerFrameVector(cx));if(!getDebuggerFrames(iter.abstractFramePtr(),&frames))returnJSTRAP_ERROR;#ifdef DEBUG/* * Validate the single-step count on this frame's script, to ensure that * we're not receiving traps we didn't ask for. Even when frames is * non-empty (and thus we know this trap was requested), do the check * anyway, to make sure the count has the correct non-zero value. * * The converse --- ensuring that we do receive traps when we should --- can * be done with unit tests. */if(iter.hasScript()){uint32_tstepperCount=0;JSScript*trappingScript=iter.script();GlobalObject*global=cx->global();if(GlobalObject::DebuggerVector*debuggers=global->getDebuggers()){for(autop=debuggers->begin();p!=debuggers->end();p++){Debugger*dbg=*p;for(FrameMap::Ranger=dbg->frames.all();!r.empty();r.popFront()){AbstractFramePtrframe=r.front().key();NativeObject*frameobj=r.front().value();if(frame.isWasmDebugFrame())continue;if(frame.script()==trappingScript&&!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined()){stepperCount++;}}}}MOZ_ASSERT(stepperCount==trappingScript->stepModeCount());}#endif// Call onStep for frames that have the handler set.for(size_ti=0;i<frames.length();i++){HandleDebuggerFrameframe=frames[i];OnStepHandler*handler=frame->onStepHandler();if(!handler)continue;Debugger*dbg=Debugger::fromChildJSObject(frame);EnterDebuggeeNoExecutenx(cx,*dbg);Maybe<AutoCompartment>ac;ac.emplace(cx,dbg->object);JSTrapStatusstatus=JSTRAP_CONTINUE;boolsuccess=handler->onStep(cx,frame,status,vp);status=dbg->processParsedHandlerResult(ac,iter.abstractFramePtr(),iter.pc(),success,status,vp);if(status!=JSTRAP_CONTINUE)returnstatus;}vp.setUndefined();if(exceptionPending)cx->setPendingException(exception);returnJSTRAP_CONTINUE;}JSTrapStatusDebugger::fireNewGlobalObject(JSContext*cx,Handle<GlobalObject*>global,MutableHandleValuevp){RootedObjecthook(cx,getHook(OnNewGlobalObject));MOZ_ASSERT(hook);MOZ_ASSERT(hook->isCallable());Maybe<AutoCompartment>ac;ac.emplace(cx,object);RootedValuewrappedGlobal(cx,ObjectValue(*global));if(!wrapDebuggeeValue(cx,&wrappedGlobal))returnreportUncaughtException(ac);// onNewGlobalObject is infallible, and thus is only allowed to return// undefined as a resumption value. If it returns anything else, we throw.// And if that happens, or if the hook itself throws, we invoke the// uncaughtExceptionHook so that we never leave an exception pending on the// cx. This allows JS_NewGlobalObject to avoid handling failures from debugger// hooks.RootedValuerv(cx);RootedValuefval(cx,ObjectValue(*hook));boolok=js::Call(cx,fval,object,wrappedGlobal,&rv);if(ok&&!rv.isUndefined()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);ok=false;}// NB: Even though we don't care about what goes into it, we have to pass vp// to handleUncaughtException so that it parses resumption values from the// uncaughtExceptionHook and tells the caller whether we should execute the// rest of the onNewGlobalObject hooks or not.JSTrapStatusstatus=ok?JSTRAP_CONTINUE:handleUncaughtException(ac,vp);MOZ_ASSERT(!cx->isExceptionPending());returnstatus;}voidDebugger::slowPathOnNewGlobalObject(JSContext*cx,Handle<GlobalObject*>global){MOZ_ASSERT(!cx->runtime()->onNewGlobalObjectWatchers().isEmpty());if(global->compartment()->creationOptions().invisibleToDebugger())return;/* * Make a copy of the runtime's onNewGlobalObjectWatchers before running the * handlers. Since one Debugger's handler can disable another's, the list * can be mutated while we're walking it. */AutoObjectVectorwatchers(cx);for(auto&dbg:cx->runtime()->onNewGlobalObjectWatchers()){MOZ_ASSERT(dbg.observesNewGlobalObject());JSObject*obj=dbg.object;JS::ExposeObjectToActiveJS(obj);if(!watchers.append(obj)){if(cx->isExceptionPending())cx->clearPendingException();return;}}JSTrapStatusstatus=JSTRAP_CONTINUE;RootedValuevalue(cx);for(size_ti=0;i<watchers.length();i++){Debugger*dbg=fromJSObject(watchers[i]);EnterDebuggeeNoExecutenx(cx,*dbg);// We disallow resumption values from onNewGlobalObject hooks, because we// want the debugger hooks for global object creation to be infallible.// But if an onNewGlobalObject hook throws, and the uncaughtExceptionHook// decides to raise an error, we want to at least avoid invoking the rest// of the onNewGlobalObject handlers in the list (not for any super// compelling reason, just because it seems like the right thing to do).// So we ignore whatever comes out in |value|, but break out of the loop// if a non-success trap status is returned.if(dbg->observesNewGlobalObject()){status=dbg->fireNewGlobalObject(cx,global,&value);if(status!=JSTRAP_CONTINUE&&status!=JSTRAP_RETURN)break;}}MOZ_ASSERT(!cx->isExceptionPending());}/* static */boolDebugger::slowPathOnLogAllocationSite(JSContext*cx,HandleObjectobj,HandleSavedFrameframe,mozilla::TimeStampwhen,GlobalObject::DebuggerVector&dbgs){MOZ_ASSERT(!dbgs.empty());mozilla::DebugOnly<ReadBarriered<Debugger*>*>begin=dbgs.begin();// Root all the Debuggers while we're iterating over them;// appendAllocationSite calls JSCompartment::wrap, and thus can GC.//// SpiderMonkey protocol is generally for the caller to prove that it has// rooted the stuff it's asking you to operate on (i.e. by passing a// Handle), but in this case, we're iterating over a global's list of// Debuggers, and globals only hold their Debuggers weakly.Rooted<GCVector<JSObject*>>activeDebuggers(cx,GCVector<JSObject*>(cx));for(autodbgp=dbgs.begin();dbgp<dbgs.end();dbgp++){if(!activeDebuggers.append((*dbgp)->object))returnfalse;}for(autodbgp=dbgs.begin();dbgp<dbgs.end();dbgp++){// The set of debuggers had better not change while we're iterating,// such that the vector gets reallocated.MOZ_ASSERT(dbgs.begin()==begin);if((*dbgp)->trackingAllocationSites&&(*dbgp)->enabled&&!(*dbgp)->appendAllocationSite(cx,obj,frame,when)){returnfalse;}}returntrue;}boolDebugger::isDebuggeeUnbarriered(constJSCompartment*compartment)const{MOZ_ASSERT(compartment);returncompartment->isDebuggee()&&debuggees.has(compartment->unsafeUnbarrieredMaybeGlobal());}boolDebugger::appendAllocationSite(JSContext*cx,HandleObjectobj,HandleSavedFrameframe,mozilla::TimeStampwhen){MOZ_ASSERT(trackingAllocationSites&&enabled);AutoCompartmentac(cx,object);RootedObjectwrappedFrame(cx,frame);if(!cx->compartment()->wrap(cx,&wrappedFrame))returnfalse;RootedAtomctorName(cx);{AutoCompartmentac(cx,obj);if(!JSObject::constructorDisplayAtom(cx,obj,&ctorName))returnfalse;}if(ctorName)cx->markAtom(ctorName);autoclassName=obj->getClass()->name;autosize=JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);autoinNursery=gc::IsInsideNursery(obj);if(!allocationsLog.emplaceBack(wrappedFrame,when,className,ctorName,size,inNursery)){ReportOutOfMemory(cx);returnfalse;}if(allocationsLog.length()>maxAllocationsLogLength){if(!allocationsLog.popFront()){ReportOutOfMemory(cx);returnfalse;}MOZ_ASSERT(allocationsLog.length()==maxAllocationsLogLength);allocationsLogOverflowed=true;}returntrue;}JSTrapStatusDebugger::firePromiseHook(JSContext*cx,Hookhook,HandleObjectpromise,MutableHandleValuevp){MOZ_ASSERT(hook==OnNewPromise||hook==OnPromiseSettled);RootedObjecthookObj(cx,getHook(hook));MOZ_ASSERT(hookObj);MOZ_ASSERT(hookObj->isCallable());Maybe<AutoCompartment>ac;ac.emplace(cx,object);RootedValuedbgObj(cx,ObjectValue(*promise));if(!wrapDebuggeeValue(cx,&dbgObj))returnreportUncaughtException(ac);// Like onNewGlobalObject, the Promise hooks are infallible and the comments// in |Debugger::fireNewGlobalObject| apply here as well.RootedValuefval(cx,ObjectValue(*hookObj));RootedValuerv(cx);boolok=js::Call(cx,fval,object,dbgObj,&rv);if(ok&&!rv.isUndefined()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);ok=false;}JSTrapStatusstatus=ok?JSTRAP_CONTINUE:handleUncaughtException(ac,vp);MOZ_ASSERT(!cx->isExceptionPending());returnstatus;}/* static */voidDebugger::slowPathPromiseHook(JSContext*cx,Hookhook,HandleObjectpromise){MOZ_ASSERT(hook==OnNewPromise||hook==OnPromiseSettled);RootedValuerval(cx);JSTrapStatusstatus=dispatchHook(cx,[hook](Debugger*dbg)->bool{returndbg->getHook(hook);},[&](Debugger*dbg)->JSTrapStatus{(void)dbg->firePromiseHook(cx,hook,promise,&rval);returnJSTRAP_CONTINUE;});if(status==JSTRAP_ERROR){// The dispatch hook function might fail to append into the list of// Debuggers which are watching for the hook.cx->clearPendingException();return;}// Promise hooks are infallible and we ignore errors from uncaught// exceptions by design.MOZ_ASSERT(status==JSTRAP_CONTINUE);}/*** Debugger code invalidation for observing execution ******************************************/classMOZ_RAIIExecutionObservableCompartments:publicDebugger::ExecutionObservableSet{HashSet<JSCompartment*>compartments_;HashSet<Zone*>zones_;public:explicitExecutionObservableCompartments(JSContext*cxMOZ_GUARD_OBJECT_NOTIFIER_PARAM):compartments_(cx),zones_(cx){MOZ_GUARD_OBJECT_NOTIFIER_INIT;}boolinit(){returncompartments_.init()&&zones_.init();}booladd(JSCompartment*comp){returncompartments_.put(comp)&&zones_.put(comp->zone());}typedefHashSet<JSCompartment*>::RangeCompartmentRange;constHashSet<JSCompartment*>*compartments()const{return&compartments_;}constHashSet<Zone*>*zones()const{return&zones_;}boolshouldRecompileOrInvalidate(JSScript*script)const{returnscript->hasBaselineScript()&&compartments_.has(script->compartment());}boolshouldMarkAsDebuggee(FrameIter&iter)const{// AbstractFramePtr can't refer to non-remateralized Ion frames or// non-debuggee wasm frames, so if iter refers to one such, we know we// don't match.returniter.hasUsableAbstractFramePtr()&&compartments_.has(iter.compartment());}MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER};// Given a particular AbstractFramePtr F that has become observable, this// represents the stack frames that need to be bailed out or marked as// debuggees, and the scripts that need to be recompiled, taking inlining into// account.classMOZ_RAIIExecutionObservableFrame:publicDebugger::ExecutionObservableSet{AbstractFramePtrframe_;public:explicitExecutionObservableFrame(AbstractFramePtrframeMOZ_GUARD_OBJECT_NOTIFIER_PARAM):frame_(frame){MOZ_GUARD_OBJECT_NOTIFIER_INIT;}Zone*singleZone()const{// We never inline across compartments, let alone across zones, so// frames_'s script's zone is the only one of interest.returnframe_.script()->compartment()->zone();}JSScript*singleScriptForZoneInvalidation()const{MOZ_CRASH("ExecutionObservableFrame shouldn't need zone-wide invalidation.");returnnullptr;}boolshouldRecompileOrInvalidate(JSScript*script)const{// Normally, *this represents exactly one script: the one frame_ is// running.//// However, debug-mode OSR uses *this for both invalidating Ion frames,// and recompiling the Baseline scripts that those Ion frames will bail// out into. Suppose frame_ is an inline frame, executing a copy of its// JSScript, S_inner, that has been inlined into the IonScript of some// other JSScript, S_outer. We must match S_outer, to decide which Ion// frame to invalidate; and we must match S_inner, to decide which// Baseline script to recompile.//// Note that this does not, by design, invalidate *all* inliners of// frame_.script(), as only frame_ is made observable, not// frame_.script().if(!script->hasBaselineScript())returnfalse;if(frame_.hasScript()&&script==frame_.script())returntrue;returnframe_.isRematerializedFrame()&&script==frame_.asRematerializedFrame()->outerScript();}boolshouldMarkAsDebuggee(FrameIter&iter)const{// AbstractFramePtr can't refer to non-remateralized Ion frames or// non-debuggee wasm frames, so if iter refers to one such, we know we// don't match.//// We never use this 'has' overload for frame invalidation, only for// frame debuggee marking; so this overload doesn't need a parallel to// the just-so inlining logic above.returniter.hasUsableAbstractFramePtr()&&iter.abstractFramePtr()==frame_;}MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER};classMOZ_RAIIExecutionObservableScript:publicDebugger::ExecutionObservableSet{RootedScriptscript_;public:ExecutionObservableScript(JSContext*cx,JSScript*scriptMOZ_GUARD_OBJECT_NOTIFIER_PARAM):script_(cx,script){MOZ_GUARD_OBJECT_NOTIFIER_INIT;}Zone*singleZone()const{returnscript_->compartment()->zone();}JSScript*singleScriptForZoneInvalidation()const{returnscript_;}boolshouldRecompileOrInvalidate(JSScript*script)const{returnscript->hasBaselineScript()&&script==script_;}boolshouldMarkAsDebuggee(FrameIter&iter)const{// AbstractFramePtr can't refer to non-remateralized Ion frames, and// while a non-rematerialized Ion frame may indeed be running script_,// we cannot mark them as debuggees until they bail out.//// Upon bailing out, any newly constructed Baseline frames that came// from Ion frames with scripts that are isDebuggee() is marked as// debuggee. This is correct in that the only other way a frame may be// marked as debuggee is via Debugger.Frame reflection, which would// have rematerialized any Ion frames.//// Also AbstractFramePtr can't refer to non-debuggee wasm frames, so if// iter refers to one such, we know we don't match.returniter.hasUsableAbstractFramePtr()&&!iter.isWasm()&&iter.abstractFramePtr().script()==script_;}MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER};/* static */boolDebugger::updateExecutionObservabilityOfFrames(JSContext*cx,constExecutionObservableSet&obs,IsObservingobserving){AutoSuppressProfilerSamplingsuppressProfilerSampling(cx);{jit::JitContextjctx(cx,nullptr);if(!jit::RecompileOnStackBaselineScriptsForDebugMode(cx,obs,observing)){ReportOutOfMemory(cx);returnfalse;}}AbstractFramePtroldestEnabledFrame;for(FrameIteriter(cx);!iter.done();++iter){if(obs.shouldMarkAsDebuggee(iter)){if(observing){if(!iter.abstractFramePtr().isDebuggee()){oldestEnabledFrame=iter.abstractFramePtr();oldestEnabledFrame.setIsDebuggee();}if(iter.abstractFramePtr().isWasmDebugFrame())iter.abstractFramePtr().asWasmDebugFrame()->observe(cx);}else{#ifdef DEBUG// Debugger.Frame lifetimes are managed by the debug epilogue,// so in general it's unsafe to unmark a frame if it has a// Debugger.Frame associated with it.MOZ_ASSERT(!inFrameMaps(iter.abstractFramePtr()));#endifiter.abstractFramePtr().unsetIsDebuggee();}}}// See comment in unsetPrevUpToDateUntil.if(oldestEnabledFrame){AutoCompartmentac(cx,oldestEnabledFrame.environmentChain());DebugEnvironments::unsetPrevUpToDateUntil(cx,oldestEnabledFrame);}returntrue;}staticinlinevoidMarkBaselineScriptActiveIfObservable(JSScript*script,constDebugger::ExecutionObservableSet&obs){if(obs.shouldRecompileOrInvalidate(script))script->baselineScript()->setActive();}staticboolAppendAndInvalidateScript(JSContext*cx,Zone*zone,JSScript*script,Vector<JSScript*>&scripts){// Enter the script's compartment as addPendingRecompile attempts to// cancel off-thread compilations, whose books are kept on the// script's compartment.MOZ_ASSERT(script->compartment()->zone()==zone);AutoCompartmentac(cx,script);zone->types.addPendingRecompile(cx,script);returnscripts.append(script);}staticboolUpdateExecutionObservabilityOfScriptsInZone(JSContext*cx,Zone*zone,constDebugger::ExecutionObservableSet&obs,Debugger::IsObservingobserving){usingnamespacejs::jit;AutoSuppressProfilerSamplingsuppressProfilerSampling(cx);FreeOp*fop=cx->runtime()->defaultFreeOp();Vector<JSScript*>scripts(cx);// Iterate through observable scripts, invalidating their Ion scripts and// appending them to a vector for discarding their baseline scripts later.{AutoEnterAnalysisenter(fop,zone);if(JSScript*script=obs.singleScriptForZoneInvalidation()){if(obs.shouldRecompileOrInvalidate(script)){if(!AppendAndInvalidateScript(cx,zone,script,scripts))returnfalse;}}else{for(autoiter=zone->cellIter<JSScript>();!iter.done();iter.next()){JSScript*script=iter;if(obs.shouldRecompileOrInvalidate(script)&&!gc::IsAboutToBeFinalizedUnbarriered(&script)){if(!AppendAndInvalidateScript(cx,zone,script,scripts))returnfalse;}}}}// Code below this point must be infallible to ensure the active bit of// BaselineScripts is in a consistent state.//// Mark active baseline scripts in the observable set so that they don't// get discarded. They will be recompiled.for(JitActivationIteratoractIter(cx,zone->group()->ownerContext());!actIter.done();++actIter){if(actIter->compartment()->zone()!=zone)continue;for(JitFrameIteratoriter(actIter);!iter.done();++iter){switch(iter.type()){caseJitFrame_BaselineJS:MarkBaselineScriptActiveIfObservable(iter.script(),obs);break;caseJitFrame_IonJS:MarkBaselineScriptActiveIfObservable(iter.script(),obs);for(InlineFrameIteratorinlineIter(cx,&iter);inlineIter.more();++inlineIter)MarkBaselineScriptActiveIfObservable(inlineIter.script(),obs);break;default:;}}}// Iterate through the scripts again and finish discarding// BaselineScripts. This must be done as a separate phase as we can only// discard the BaselineScript on scripts that have no IonScript.for(size_ti=0;i<scripts.length();i++){MOZ_ASSERT_IF(scripts[i]->isDebuggee(),observing);FinishDiscardBaselineScript(fop,scripts[i]);}// Iterate through all wasm instances to find ones that need to be updated.for(JSCompartment*c:zone->compartments()){for(wasm::Instance*instance:c->wasm.instances()){if(!instance->debugEnabled())continue;boolenableTrap=observing==Debugger::IsObserving::Observing;instance->ensureEnterFrameTrapsState(cx,enableTrap);}}returntrue;}/* static */boolDebugger::updateExecutionObservabilityOfScripts(JSContext*cx,constExecutionObservableSet&obs,IsObservingobserving){if(Zone*zone=obs.singleZone())returnUpdateExecutionObservabilityOfScriptsInZone(cx,zone,obs,observing);typedefExecutionObservableSet::ZoneRangeZoneRange;for(ZoneRanger=obs.zones()->all();!r.empty();r.popFront()){if(!UpdateExecutionObservabilityOfScriptsInZone(cx,r.front(),obs,observing))returnfalse;}returntrue;}template<typenameFrameFn>/* static */voidDebugger::forEachDebuggerFrame(AbstractFramePtrframe,FrameFnfn){GlobalObject*global=frame.global();if(GlobalObject::DebuggerVector*debuggers=global->getDebuggers()){for(autop=debuggers->begin();p!=debuggers->end();p++){Debugger*dbg=*p;if(FrameMap::Ptrentry=dbg->frames.lookup(frame))fn(entry->value());}}}/* static */boolDebugger::getDebuggerFrames(AbstractFramePtrframe,MutableHandle<DebuggerFrameVector>frames){boolhadOOM=false;forEachDebuggerFrame(frame,[&](DebuggerFrame*frameobj){if(!hadOOM&&!frames.append(frameobj))hadOOM=true;});return!hadOOM;}/* static */boolDebugger::updateExecutionObservability(JSContext*cx,ExecutionObservableSet&obs,IsObservingobserving){if(!obs.singleZone()&&obs.zones()->empty())returntrue;// Invalidate scripts first so we can set the needsArgsObj flag on scripts// before patching frames.returnupdateExecutionObservabilityOfScripts(cx,obs,observing)&&updateExecutionObservabilityOfFrames(cx,obs,observing);}/* static */boolDebugger::ensureExecutionObservabilityOfScript(JSContext*cx,JSScript*script){if(script->isDebuggee())returntrue;ExecutionObservableScriptobs(cx,script);returnupdateExecutionObservability(cx,obs,Observing);}/* static */boolDebugger::ensureExecutionObservabilityOfOsrFrame(JSContext*cx,InterpreterFrame*frame){MOZ_ASSERT(frame->isDebuggee());if(frame->script()->hasBaselineScript()&&frame->script()->baselineScript()->hasDebugInstrumentation()){returntrue;}ExecutionObservableFrameobs(frame);returnupdateExecutionObservabilityOfFrames(cx,obs,Observing);}/* static */boolDebugger::ensureExecutionObservabilityOfFrame(JSContext*cx,AbstractFramePtrframe){MOZ_ASSERT_IF(frame.hasScript()&&frame.script()->isDebuggee(),frame.isDebuggee());MOZ_ASSERT_IF(frame.isWasmDebugFrame(),frame.wasmInstance()->debugEnabled());if(frame.isDebuggee())returntrue;ExecutionObservableFrameobs(frame);returnupdateExecutionObservabilityOfFrames(cx,obs,Observing);}/* static */boolDebugger::ensureExecutionObservabilityOfCompartment(JSContext*cx,JSCompartment*comp){if(comp->debuggerObservesAllExecution())returntrue;ExecutionObservableCompartmentsobs(cx);if(!obs.init()||!obs.add(comp))returnfalse;comp->updateDebuggerObservesAllExecution();returnupdateExecutionObservability(cx,obs,Observing);}/* static */boolDebugger::hookObservesAllExecution(Hookwhich){returnwhich==OnEnterFrame;}Debugger::IsObservingDebugger::observesAllExecution()const{if(enabled&&!!getHook(OnEnterFrame))returnObserving;returnNotObserving;}Debugger::IsObservingDebugger::observesAsmJS()const{if(enabled&&!allowUnobservedAsmJS)returnObserving;returnNotObserving;}Debugger::IsObservingDebugger::observesBinarySource()const{if(enabled&&allowWasmBinarySource)returnObserving;returnNotObserving;}Debugger::IsObservingDebugger::observesCoverage()const{if(enabled&&collectCoverageInfo)returnObserving;returnNotObserving;}// Toggle whether this Debugger's debuggees observe all execution. This is// called when a hook that observes all execution is set or unset. See// hookObservesAllExecution.boolDebugger::updateObservesAllExecutionOnDebuggees(JSContext*cx,IsObservingobserving){ExecutionObservableCompartmentsobs(cx);if(!obs.init())returnfalse;for(WeakGlobalObjectSet::Ranger=debuggees.all();!r.empty();r.popFront()){GlobalObject*global=r.front();JSCompartment*comp=global->compartment();if(comp->debuggerObservesAllExecution()==observing)continue;// It's expensive to eagerly invalidate and recompile a compartment,// so add the compartment to the set only if we are observing.if(observing&&!obs.add(comp))returnfalse;}if(!updateExecutionObservability(cx,obs,observing))returnfalse;typedefExecutionObservableCompartments::CompartmentRangeCompartmentRange;for(CompartmentRanger=obs.compartments()->all();!r.empty();r.popFront())r.front()->updateDebuggerObservesAllExecution();returntrue;}boolDebugger::updateObservesCoverageOnDebuggees(JSContext*cx,IsObservingobserving){ExecutionObservableCompartmentsobs(cx);if(!obs.init())returnfalse;for(WeakGlobalObjectSet::Ranger=debuggees.all();!r.empty();r.popFront()){GlobalObject*global=r.front();JSCompartment*comp=global->compartment();if(comp->debuggerObservesCoverage()==observing)continue;// Invalidate and recompile a compartment to add or remove PCCounts// increments. We have to eagerly invalidate, as otherwise we might have// dangling pointers to freed PCCounts.if(!obs.add(comp))returnfalse;}// If any frame on the stack belongs to the debuggee, then we cannot update// the ScriptCounts, because this would imply to invalidate a Debugger.Frame// to recompile it with/without ScriptCount support.for(FrameIteriter(cx);!iter.done();++iter){if(obs.shouldMarkAsDebuggee(iter)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_NOT_IDLE);returnfalse;}}if(!updateExecutionObservability(cx,obs,observing))returnfalse;// All compartments can safely be toggled, and all scripts will be// recompiled. Thus we can update each compartment accordingly.typedefExecutionObservableCompartments::CompartmentRangeCompartmentRange;for(CompartmentRanger=obs.compartments()->all();!r.empty();r.popFront())r.front()->updateDebuggerObservesCoverage();returntrue;}voidDebugger::updateObservesAsmJSOnDebuggees(IsObservingobserving){for(WeakGlobalObjectSet::Ranger=debuggees.all();!r.empty();r.popFront()){GlobalObject*global=r.front();JSCompartment*comp=global->compartment();if(comp->debuggerObservesAsmJS()==observing)continue;comp->updateDebuggerObservesAsmJS();}}voidDebugger::updateObservesBinarySourceDebuggees(IsObservingobserving){for(WeakGlobalObjectSet::Ranger=debuggees.all();!r.empty();r.popFront()){GlobalObject*global=r.front();JSCompartment*comp=global->compartment();if(comp->debuggerObservesBinarySource()==observing)continue;comp->updateDebuggerObservesBinarySource();}}/*** Allocations Tracking *************************************************************************//* static */boolDebugger::cannotTrackAllocations(constGlobalObject&global){autoexistingCallback=global.compartment()->getAllocationMetadataBuilder();returnexistingCallback&&existingCallback!=&SavedStacks::metadataBuilder;}/* static */boolDebugger::isObservedByDebuggerTrackingAllocations(constGlobalObject&debuggee){if(auto*v=debuggee.getDebuggers()){for(autop=v->begin();p!=v->end();p++){if((*p)->trackingAllocationSites&&(*p)->enabled){returntrue;}}}returnfalse;}/* static */boolDebugger::addAllocationsTracking(JSContext*cx,Handle<GlobalObject*>debuggee){// Precondition: the given global object is being observed by at least one// Debugger that is tracking allocations.MOZ_ASSERT(isObservedByDebuggerTrackingAllocations(*debuggee));if(Debugger::cannotTrackAllocations(*debuggee)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);returnfalse;}debuggee->compartment()->setAllocationMetadataBuilder(&SavedStacks::metadataBuilder);debuggee->compartment()->chooseAllocationSamplingProbability();returntrue;}/* static */voidDebugger::removeAllocationsTracking(GlobalObject&global){// If there are still Debuggers that are observing allocations, we cannot// remove the metadata callback yet. Recompute the sampling probability// based on the remaining debuggers' needs.if(isObservedByDebuggerTrackingAllocations(global)){global.compartment()->chooseAllocationSamplingProbability();return;}global.compartment()->forgetAllocationMetadataBuilder();}boolDebugger::addAllocationsTrackingForAllDebuggees(JSContext*cx){MOZ_ASSERT(trackingAllocationSites);// We don't want to end up in a state where we added allocations// tracking to some of our debuggees, but failed to do so for// others. Before attempting to start tracking allocations in *any* of// our debuggees, ensure that we will be able to track allocations for// *all* of our debuggees.for(WeakGlobalObjectSet::Ranger=debuggees.all();!r.empty();r.popFront()){if(Debugger::cannotTrackAllocations(*r.front().get())){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);returnfalse;}}Rooted<GlobalObject*>g(cx);for(WeakGlobalObjectSet::Ranger=debuggees.all();!r.empty();r.popFront()){// This should always succeed, since we already checked for the// error case above.g=r.front().get();MOZ_ALWAYS_TRUE(Debugger::addAllocationsTracking(cx,g));}returntrue;}voidDebugger::removeAllocationsTrackingForAllDebuggees(){for(WeakGlobalObjectSet::Ranger=debuggees.all();!r.empty();r.popFront())Debugger::removeAllocationsTracking(*r.front().get());allocationsLog.clear();}/*** Debugger JSObjects **************************************************************************/voidDebugger::traceCrossCompartmentEdges(JSTracer*trc){objects.traceCrossCompartmentEdges<DebuggerObject_trace>(trc);environments.traceCrossCompartmentEdges<DebuggerEnv_trace>(trc);scripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);sources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);wasmInstanceScripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);wasmInstanceSources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);}/* * Ordinarily, WeakMap keys and values are marked because at some point it was * discovered that the WeakMap was live; that is, some object containing the * WeakMap was marked during mark phase. * * However, during zone GC, we have to do something about cross-compartment * edges in non-GC'd compartments. Since the source may be live, we * conservatively assume it is and mark the edge. * * Each Debugger object keeps four cross-compartment WeakMaps: objects, scripts, * script source objects, and environments. They have the property that all * their values are in the same compartment as the Debugger object, but we have * to mark the keys and the private pointer in the wrapper object. * * We must scan all Debugger objects regardless of whether they *currently* have * any debuggees in a compartment being GC'd, because the WeakMap entries * persist even when debuggees are removed. * * This happens during the initial mark phase, not iterative marking, because * all the edges being reported here are strong references. * * This method is also used during compacting GC to update cross compartment * pointers into zones that are being compacted. *//* static */voidDebugger::traceIncomingCrossCompartmentEdges(JSTracer*trc){JSRuntime*rt=trc->runtime();gc::Statestate=rt->gc.state();MOZ_ASSERT(state==gc::State::MarkRoots||state==gc::State::Compact);for(ZoneGroupsItergroup(rt);!group.done();group.next()){for(Debugger*dbg:group->debuggerList()){Zone*zone=MaybeForwarded(dbg->object.get())->zone();if(!zone->isCollecting()||state==gc::State::Compact)dbg->traceCrossCompartmentEdges(trc);}}}/* * This method has two tasks: * 1. Mark Debugger objects that are unreachable except for debugger hooks that * may yet be called. * 2. Mark breakpoint handlers. * * This happens during the iterative part of the GC mark phase. This method * returns true if it has to mark anything; GC calls it repeatedly until it * returns false. *//* static */boolDebugger::markIteratively(GCMarker*marker){boolmarkedAny=false;/* * Find all Debugger objects in danger of GC. This code is a little * convoluted since the easiest way to find them is via their debuggees. */JSRuntime*rt=marker->runtime();for(CompartmentsIterc(rt,SkipAtoms);!c.done();c.next()){if(c->isDebuggee()){GlobalObject*global=c->unsafeUnbarrieredMaybeGlobal();if(!IsMarkedUnbarriered(rt,&global))continue;/* * Every debuggee has at least one debugger, so in this case * getDebuggers can't return nullptr. */constGlobalObject::DebuggerVector*debuggers=global->getDebuggers();MOZ_ASSERT(debuggers);for(autop=debuggers->begin();p!=debuggers->end();p++){Debugger*dbg=*p;/* * dbg is a Debugger with at least one debuggee. Check three things: * - dbg is actually in a compartment that is being marked * - it isn't already marked * - it actually has hooks that might be called */GCPtrNativeObject&dbgobj=dbg->toJSObjectRef();if(!dbgobj->zone()->isGCMarking())continue;booldbgMarked=IsMarked(rt,&dbgobj);if(!dbgMarked&&dbg->hasAnyLiveHooks(rt)){/* * obj could be reachable only via its live, enabled * debugger hooks, which may yet be called. */TraceEdge(marker,&dbgobj,"enabled Debugger");markedAny=true;dbgMarked=true;}if(dbgMarked){/* Search for breakpoints to mark. */for(Breakpoint*bp=dbg->firstBreakpoint();bp;bp=bp->nextInDebugger()){switch(bp->site->type()){caseBreakpointSite::Type::JS:if(IsMarkedUnbarriered(rt,&bp->site->asJS()->script)){/* * The debugger and the script are both live. * Therefore the breakpoint handler is live. */if(!IsMarked(rt,&bp->getHandlerRef())){TraceEdge(marker,&bp->getHandlerRef(),"breakpoint handler");markedAny=true;}}break;caseBreakpointSite::Type::Wasm:if(IsMarkedUnbarriered(rt,&bp->asWasm()->wasmInstance)){/* * The debugger and the wasm instance are both live. * Therefore the breakpoint handler is live. */if(!IsMarked(rt,&bp->getHandlerRef())){TraceEdge(marker,&bp->getHandlerRef(),"wasm breakpoint handler");markedAny=true;}}break;}}}}}}returnmarkedAny;}/* static */voidDebugger::traceAllForMovingGC(JSTracer*trc){JSRuntime*rt=trc->runtime();for(ZoneGroupsItergroup(rt);!group.done();group.next()){for(Debugger*dbg:group->debuggerList())dbg->traceForMovingGC(trc);}}/* * Trace all debugger-owned GC things unconditionally. This is used during * compacting GC and in minor GC: the minor GC cannot apply the weak constraints * of the full GC because it visits only part of the heap. */voidDebugger::traceForMovingGC(JSTracer*trc){trace(trc);for(WeakGlobalObjectSet::Enume(debuggees);!e.empty();e.popFront())TraceManuallyBarrieredEdge(trc,e.mutableFront().unsafeGet(),"Global Object");for(Breakpoint*bp=firstBreakpoint();bp;bp=bp->nextInDebugger()){switch(bp->site->type()){caseBreakpointSite::Type::JS:TraceManuallyBarrieredEdge(trc,&bp->site->asJS()->script,"breakpoint script");break;caseBreakpointSite::Type::Wasm:TraceManuallyBarrieredEdge(trc,&bp->asWasm()->wasmInstance,"breakpoint wasm instance");break;}TraceEdge(trc,&bp->getHandlerRef(),"breakpoint handler");}}/* static */voidDebugger::traceObject(JSTracer*trc,JSObject*obj){if(Debugger*dbg=Debugger::fromJSObject(obj))dbg->trace(trc);}voidDebugger::trace(JSTracer*trc){TraceEdge(trc,&object,"Debugger Object");TraceNullableEdge(trc,&uncaughtExceptionHook,"hooks");/* * Mark Debugger.Frame objects. These are all reachable from JS, because the * corresponding JS frames are still on the stack. * * (Once we support generator frames properly, we will need * weakly-referenced Debugger.Frame objects as well, for suspended generator * frames.) */if(frames.initialized()){for(FrameMap::Ranger=frames.all();!r.empty();r.popFront()){HeapPtr<DebuggerFrame*>&frameobj=r.front().value();MOZ_ASSERT(MaybeForwarded(frameobj.get())->getPrivate());TraceEdge(trc,&frameobj,"live Debugger.Frame");}}allocationsLog.trace(trc);/* Trace the weak map from JSScript instances to Debugger.Script objects. */scripts.trace(trc);/* Trace the referent -> Debugger.Source weak map */sources.trace(trc);/* Trace the referent -> Debugger.Object weak map. */objects.trace(trc);/* Trace the referent -> Debugger.Environment weak map. */environments.trace(trc);/* Trace the WasmInstanceObject -> synthesized Debugger.Script weak map. */wasmInstanceScripts.trace(trc);/* Trace the WasmInstanceObject -> synthesized Debugger.Source weak map. */wasmInstanceSources.trace(trc);}/* static */voidDebugger::sweepAll(FreeOp*fop){JSRuntime*rt=fop->runtime();for(ZoneGroupsItergroup(rt);!group.done();group.next()){Debugger*dbg=group->debuggerList().getFirst();while(dbg){Debugger*next=dbg->getNext();// Detach dying debuggers and debuggees from each other. Since this// requires access to both objects it must be done before either// object is finalized.booldebuggerDying=IsAboutToBeFinalized(&dbg->object);for(WeakGlobalObjectSet::Enume(dbg->debuggees);!e.empty();e.popFront()){GlobalObject*global=e.front().unbarrieredGet();if(debuggerDying||IsAboutToBeFinalizedUnbarriered(&global))dbg->removeDebuggeeGlobal(fop,e.front().unbarrieredGet(),&e);}if(debuggerDying)fop->delete_(dbg);dbg=next;}}}/* static */voidDebugger::detachAllDebuggersFromGlobal(FreeOp*fop,GlobalObject*global){constGlobalObject::DebuggerVector*debuggers=global->getDebuggers();MOZ_ASSERT(!debuggers->empty());while(!debuggers->empty())debuggers->back()->removeDebuggeeGlobal(fop,global,nullptr);}/* static */voidDebugger::findZoneEdges(Zone*zone,js::gc::ZoneComponentFinder&finder){/* * For debugger cross compartment wrappers, add edges in the opposite * direction to those already added by JSCompartment::findOutgoingEdges. * This ensure that debuggers and their debuggees are finalized in the same * group. */for(ZoneGroupsItergroup(zone->runtimeFromActiveCooperatingThread());!group.done();group.next()){for(Debugger*dbg:group->debuggerList()){Zone*w=dbg->object->zone();if(w==zone||!w->isGCMarking())continue;if(dbg->debuggeeZones.has(zone)||dbg->scripts.hasKeyInZone(zone)||dbg->sources.hasKeyInZone(zone)||dbg->objects.hasKeyInZone(zone)||dbg->environments.hasKeyInZone(zone)||dbg->wasmInstanceScripts.hasKeyInZone(zone)||dbg->wasmInstanceSources.hasKeyInZone(zone)){finder.addEdgeTo(w);}}}}constClassOpsDebugger::classOps_={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */nullptr,/* enumerate */nullptr,/* newEnumerate */nullptr,/* resolve */nullptr,/* mayResolve */nullptr,/* finalize */nullptr,/* call */nullptr,/* hasInstance */nullptr,/* construct */Debugger::traceObject};constClassDebugger::class_={"Debugger",JSCLASS_HAS_PRIVATE|JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),&Debugger::classOps_};staticDebugger*Debugger_fromThisValue(JSContext*cx,constCallArgs&args,constchar*fnname){JSObject*thisobj=NonNullObject(cx,args.thisv());if(!thisobj)returnnullptr;if(thisobj->getClass()!=&Debugger::class_){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger",fnname,thisobj->getClass()->name);returnnullptr;}/* * Forbid Debugger.prototype, which is of the Debugger JSClass but isn't * really a Debugger object. The prototype object is distinguished by * having a nullptr private value. */Debugger*dbg=Debugger::fromJSObject(thisobj);if(!dbg){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger",fnname,"prototype object");}returndbg;}#define THIS_DEBUGGER(cx, argc, vp, fnname, args, dbg) \ CallArgs args = CallArgsFromVp(argc, vp); \ Debugger* dbg = Debugger_fromThisValue(cx, args, fnname); \ if (!dbg) \ return false/* static */boolDebugger::getEnabled(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"get enabled",args,dbg);args.rval().setBoolean(dbg->enabled);returntrue;}/* static */boolDebugger::setEnabled(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"set enabled",args,dbg);if(!args.requireAtLeast(cx,"Debugger.set enabled",1))returnfalse;boolwasEnabled=dbg->enabled;dbg->enabled=ToBoolean(args[0]);if(wasEnabled!=dbg->enabled){if(dbg->trackingAllocationSites){if(wasEnabled){dbg->removeAllocationsTrackingForAllDebuggees();}else{if(!dbg->addAllocationsTrackingForAllDebuggees(cx)){dbg->enabled=false;returnfalse;}}}for(Breakpoint*bp=dbg->firstBreakpoint();bp;bp=bp->nextInDebugger()){if(!wasEnabled)bp->site->inc(cx->runtime()->defaultFreeOp());elsebp->site->dec(cx->runtime()->defaultFreeOp());}/* * Add or remove ourselves from the runtime's list of Debuggers * that care about new globals. */if(dbg->getHook(OnNewGlobalObject)){if(!wasEnabled){cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);}else{cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);}}// Ensure the compartment is observable if we are re-enabling a// Debugger with hooks that observe all execution.if(!dbg->updateObservesAllExecutionOnDebuggees(cx,dbg->observesAllExecution()))returnfalse;// Note: To toogle code coverage, we currently need to have no live// stack frame, thus the coverage does not depend on the enabled flag.dbg->updateObservesAsmJSOnDebuggees(dbg->observesAsmJS());dbg->updateObservesBinarySourceDebuggees(dbg->observesBinarySource());}args.rval().setUndefined();returntrue;}/* static */boolDebugger::getHookImpl(JSContext*cx,CallArgs&args,Debugger&dbg,Hookwhich){MOZ_ASSERT(which>=0&&which<HookCount);args.rval().set(dbg.object->getReservedSlot(JSSLOT_DEBUG_HOOK_START+which));returntrue;}/* static */boolDebugger::setHookImpl(JSContext*cx,CallArgs&args,Debugger&dbg,Hookwhich){MOZ_ASSERT(which>=0&&which<HookCount);if(!args.requireAtLeast(cx,"Debugger.setHook",1))returnfalse;if(args[0].isObject()){if(!args[0].toObject().isCallable())returnReportIsNotFunction(cx,args[0],args.length()-1);}elseif(!args[0].isUndefined()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_CALLABLE_OR_UNDEFINED);returnfalse;}uint32_tslot=JSSLOT_DEBUG_HOOK_START+which;RootedValueoldHook(cx,dbg.object->getReservedSlot(slot));dbg.object->setReservedSlot(slot,args[0]);if(hookObservesAllExecution(which)){if(!dbg.updateObservesAllExecutionOnDebuggees(cx,dbg.observesAllExecution())){dbg.object->setReservedSlot(slot,oldHook);returnfalse;}}args.rval().setUndefined();returntrue;}/* static */boolDebugger::getOnDebuggerStatement(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(get onDebuggerStatement)",args,dbg);returngetHookImpl(cx,args,*dbg,OnDebuggerStatement);}/* static */boolDebugger::setOnDebuggerStatement(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(set onDebuggerStatement)",args,dbg);returnsetHookImpl(cx,args,*dbg,OnDebuggerStatement);}/* static */boolDebugger::getOnExceptionUnwind(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(get onExceptionUnwind)",args,dbg);returngetHookImpl(cx,args,*dbg,OnExceptionUnwind);}/* static */boolDebugger::setOnExceptionUnwind(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(set onExceptionUnwind)",args,dbg);returnsetHookImpl(cx,args,*dbg,OnExceptionUnwind);}/* static */boolDebugger::getOnNewScript(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(get onNewScript)",args,dbg);returngetHookImpl(cx,args,*dbg,OnNewScript);}/* static */boolDebugger::setOnNewScript(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(set onNewScript)",args,dbg);returnsetHookImpl(cx,args,*dbg,OnNewScript);}/* static */boolDebugger::getOnNewPromise(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(get onNewPromise)",args,dbg);returngetHookImpl(cx,args,*dbg,OnNewPromise);}/* static */boolDebugger::setOnNewPromise(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(set onNewPromise)",args,dbg);returnsetHookImpl(cx,args,*dbg,OnNewPromise);}/* static */boolDebugger::getOnPromiseSettled(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(get onPromiseSettled)",args,dbg);returngetHookImpl(cx,args,*dbg,OnPromiseSettled);}/* static */boolDebugger::setOnPromiseSettled(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(set onPromiseSettled)",args,dbg);returnsetHookImpl(cx,args,*dbg,OnPromiseSettled);}/* static */boolDebugger::getOnEnterFrame(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(get onEnterFrame)",args,dbg);returngetHookImpl(cx,args,*dbg,OnEnterFrame);}/* static */boolDebugger::setOnEnterFrame(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(set onEnterFrame)",args,dbg);returnsetHookImpl(cx,args,*dbg,OnEnterFrame);}/* static */boolDebugger::getOnNewGlobalObject(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"(get onNewGlobalObject)",args,dbg);returngetHookImpl(cx,args,*dbg,OnNewGlobalObject);}/* static */boolDebugger::setOnNewGlobalObject(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"setOnNewGlobalObject",args,dbg);RootedObjectoldHook(cx,dbg->getHook(OnNewGlobalObject));if(!setHookImpl(cx,args,*dbg,OnNewGlobalObject))returnfalse;/* * Add or remove ourselves from the runtime's list of Debuggers that * care about new globals. */if(dbg->enabled){JSObject*newHook=dbg->getHook(OnNewGlobalObject);if(!oldHook&&newHook){cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);}elseif(oldHook&&!newHook){cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);}}returntrue;}/* static */boolDebugger::getUncaughtExceptionHook(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"get uncaughtExceptionHook",args,dbg);args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);returntrue;}/* static */boolDebugger::setUncaughtExceptionHook(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"set uncaughtExceptionHook",args,dbg);if(!args.requireAtLeast(cx,"Debugger.set uncaughtExceptionHook",1))returnfalse;if(!args[0].isNull()&&(!args[0].isObject()||!args[0].toObject().isCallable())){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_ASSIGN_FUNCTION_OR_NULL,"uncaughtExceptionHook");returnfalse;}dbg->uncaughtExceptionHook=args[0].toObjectOrNull();args.rval().setUndefined();returntrue;}/* static */boolDebugger::getAllowUnobservedAsmJS(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"get allowUnobservedAsmJS",args,dbg);args.rval().setBoolean(dbg->allowUnobservedAsmJS);returntrue;}/* static */boolDebugger::setAllowUnobservedAsmJS(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"set allowUnobservedAsmJS",args,dbg);if(!args.requireAtLeast(cx,"Debugger.set allowUnobservedAsmJS",1))returnfalse;dbg->allowUnobservedAsmJS=ToBoolean(args[0]);for(WeakGlobalObjectSet::Ranger=dbg->debuggees.all();!r.empty();r.popFront()){GlobalObject*global=r.front();JSCompartment*comp=global->compartment();comp->updateDebuggerObservesAsmJS();}args.rval().setUndefined();returntrue;}/* static */boolDebugger::getAllowWasmBinarySource(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"get allowWasmBinarySource",args,dbg);args.rval().setBoolean(dbg->allowWasmBinarySource);returntrue;}/* static */boolDebugger::setAllowWasmBinarySource(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"set allowWasmBinarySource",args,dbg);if(!args.requireAtLeast(cx,"Debugger.set allowWasmBinarySource",1))returnfalse;dbg->allowWasmBinarySource=ToBoolean(args[0]);for(WeakGlobalObjectSet::Ranger=dbg->debuggees.all();!r.empty();r.popFront()){GlobalObject*global=r.front();JSCompartment*comp=global->compartment();comp->updateDebuggerObservesBinarySource();}args.rval().setUndefined();returntrue;}/* static */boolDebugger::getCollectCoverageInfo(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"get collectCoverageInfo",args,dbg);args.rval().setBoolean(dbg->collectCoverageInfo);returntrue;}/* static */boolDebugger::setCollectCoverageInfo(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"set collectCoverageInfo",args,dbg);if(!args.requireAtLeast(cx,"Debugger.set collectCoverageInfo",1))returnfalse;dbg->collectCoverageInfo=ToBoolean(args[0]);IsObservingobserving=dbg->collectCoverageInfo?Observing:NotObserving;if(!dbg->updateObservesCoverageOnDebuggees(cx,observing))returnfalse;args.rval().setUndefined();returntrue;}/* static */boolDebugger::getMemory(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"get memory",args,dbg);ValuememoryValue=dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);if(!memoryValue.isObject()){RootedObjectmemory(cx,DebuggerMemory::create(cx,dbg));if(!memory)returnfalse;memoryValue=ObjectValue(*memory);}args.rval().set(memoryValue);returntrue;}/* * Given a value used to designate a global (there's quite a variety; see the * docs), return the actual designee. * * Note that this does not check whether the designee is marked "invisible to * Debugger" or not; different callers need to handle invisible-to-Debugger * globals in different ways. */GlobalObject*Debugger::unwrapDebuggeeArgument(JSContext*cx,constValue&v){if(!v.isObject()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,"argument","not a global object");returnnullptr;}RootedObjectobj(cx,&v.toObject());/* If it's a Debugger.Object belonging to this debugger, dereference that. */if(obj->getClass()==&DebuggerObject::class_){RootedValuerv(cx,v);if(!unwrapDebuggeeValue(cx,&rv))returnnullptr;obj=&rv.toObject();}/* If we have a cross-compartment wrapper, dereference as far as is secure. */obj=CheckedUnwrap(obj);if(!obj){ReportAccessDenied(cx);returnnullptr;}/* If that produced a WindowProxy, get the Window (global). */obj=ToWindowIfWindowProxy(obj);/* If that didn't produce a global object, it's an error. */if(!obj->is<GlobalObject>()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,"argument","not a global object");returnnullptr;}return&obj->as<GlobalObject>();}/* static */boolDebugger::addDebuggee(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"addDebuggee",args,dbg);if(!args.requireAtLeast(cx,"Debugger.addDebuggee",1))returnfalse;Rooted<GlobalObject*>global(cx,dbg->unwrapDebuggeeArgument(cx,args[0]));if(!global)returnfalse;if(!dbg->addDebuggeeGlobal(cx,global))returnfalse;RootedValuev(cx,ObjectValue(*global));if(!dbg->wrapDebuggeeValue(cx,&v))returnfalse;args.rval().set(v);returntrue;}/* static */boolDebugger::addAllGlobalsAsDebuggees(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"addAllGlobalsAsDebuggees",args,dbg);for(ZonesIterzone(cx->runtime(),SkipAtoms);!zone.done();zone.next()){for(CompartmentsInZoneIterc(zone);!c.done();c.next()){if(c==dbg->object->compartment()||c->creationOptions().invisibleToDebugger())continue;c->scheduledForDestruction=false;GlobalObject*global=c->maybeGlobal();if(global){Rooted<GlobalObject*>rg(cx,global);if(!dbg->addDebuggeeGlobal(cx,rg))returnfalse;}}}args.rval().setUndefined();returntrue;}/* static */boolDebugger::removeDebuggee(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"removeDebuggee",args,dbg);if(!args.requireAtLeast(cx,"Debugger.removeDebuggee",1))returnfalse;Rooted<GlobalObject*>global(cx,dbg->unwrapDebuggeeArgument(cx,args[0]));if(!global)returnfalse;ExecutionObservableCompartmentsobs(cx);if(!obs.init())returnfalse;if(dbg->debuggees.has(global)){dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(),global,nullptr);// Only update the compartment if there are no Debuggers left, as it's// expensive to check if no other Debugger has a live script or frame hook// on any of the current on-stack debuggee frames.if(global->getDebuggers()->empty()&&!obs.add(global->compartment()))returnfalse;if(!updateExecutionObservability(cx,obs,NotObserving))returnfalse;}args.rval().setUndefined();returntrue;}/* static */boolDebugger::removeAllDebuggees(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"removeAllDebuggees",args,dbg);ExecutionObservableCompartmentsobs(cx);if(!obs.init())returnfalse;for(WeakGlobalObjectSet::Enume(dbg->debuggees);!e.empty();e.popFront()){Rooted<GlobalObject*>global(cx,e.front());dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(),global,&e);// See note about adding to the observable set in removeDebuggee.if(global->getDebuggers()->empty()&&!obs.add(global->compartment()))returnfalse;}if(!updateExecutionObservability(cx,obs,NotObserving))returnfalse;args.rval().setUndefined();returntrue;}/* static */boolDebugger::hasDebuggee(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"hasDebuggee",args,dbg);if(!args.requireAtLeast(cx,"Debugger.hasDebuggee",1))returnfalse;GlobalObject*global=dbg->unwrapDebuggeeArgument(cx,args[0]);if(!global)returnfalse;args.rval().setBoolean(!!dbg->debuggees.lookup(global));returntrue;}/* static */boolDebugger::getDebuggees(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"getDebuggees",args,dbg);// Obtain the list of debuggees before wrapping each debuggee, as a GC could// update the debuggees set while we are iterating it.unsignedcount=dbg->debuggees.count();AutoValueVectordebuggees(cx);if(!debuggees.resize(count))returnfalse;unsignedi=0;{JS::AutoCheckCannotGCnogc;for(WeakGlobalObjectSet::Enume(dbg->debuggees);!e.empty();e.popFront())debuggees[i++].setObject(*e.front().get());}RootedArrayObjectarrobj(cx,NewDenseFullyAllocatedArray(cx,count));if(!arrobj)returnfalse;arrobj->ensureDenseInitializedLength(cx,0,count);for(i=0;i<count;i++){RootedValuev(cx,debuggees[i]);if(!dbg->wrapDebuggeeValue(cx,&v))returnfalse;arrobj->setDenseElement(i,v);}args.rval().setObject(*arrobj);returntrue;}/* static */boolDebugger::getNewestFrame(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"getNewestFrame",args,dbg);/* Since there may be multiple contexts, use AllFramesIter. */for(AllFramesIteri(cx);!i.done();++i){if(dbg->observesFrame(i)){// Ensure that Ion frames are rematerialized. Only rematerialized// Ion frames may be used as AbstractFramePtrs.if(i.isIon()&&!i.ensureHasRematerializedFrame(cx))returnfalse;AbstractFramePtrframe=i.abstractFramePtr();FrameIteriter(i.activation()->cx());while(!iter.hasUsableAbstractFramePtr()||iter.abstractFramePtr()!=frame)++iter;returndbg->getScriptFrame(cx,iter,args.rval());}}args.rval().setNull();returntrue;}/* static */boolDebugger::clearAllBreakpoints(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"clearAllBreakpoints",args,dbg);for(WeakGlobalObjectSet::Ranger=dbg->debuggees.all();!r.empty();r.popFront())r.front()->compartment()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(),dbg,nullptr);returntrue;}/* static */boolDebugger::construct(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);/* Check that the arguments, if any, are cross-compartment wrappers. */for(unsignedi=0;i<args.length();i++){JSObject*argobj=NonNullObject(cx,args[i]);if(!argobj)returnfalse;if(!argobj->is<CrossCompartmentWrapperObject>()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_CCW_REQUIRED,"Debugger");returnfalse;}}/* Get Debugger.prototype. */RootedValuev(cx);RootedObjectcallee(cx,&args.callee());if(!GetProperty(cx,callee,callee,cx->names().prototype,&v))returnfalse;RootedNativeObjectproto(cx,&v.toObject().as<NativeObject>());MOZ_ASSERT(proto->getClass()==&Debugger::class_);/* * Make the new Debugger object. Each one has a reference to * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The * rest of the reserved slots are for hooks; they default to undefined. */RootedNativeObjectobj(cx,NewNativeObjectWithGivenProto(cx,&Debugger::class_,proto,TenuredObject));if(!obj)returnfalse;for(unsignedslot=JSSLOT_DEBUG_PROTO_START;slot<JSSLOT_DEBUG_PROTO_STOP;slot++)obj->setReservedSlot(slot,proto->getReservedSlot(slot));obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE,NullValue());// Debuggers currently require single threaded execution. A debugger may be// used to debug content in other zone groups, and may be used to observe// all activity in the runtime via hooks like OnNewGlobalObject.if(!cx->runtime()->beginSingleThreadedExecution(cx)){JS_ReportErrorASCII(cx,"Cannot ensure single threaded execution in Debugger");returnfalse;}Debugger*debugger;{/* Construct the underlying C++ object. */autodbg=cx->make_unique<Debugger>(cx,obj.get());if(!dbg){JS::AutoSuppressGCAnalysisnogc;// Suppress warning about |dbg|.cx->runtime()->endSingleThreadedExecution(cx);returnfalse;}if(!dbg->init(cx))returnfalse;debugger=dbg.release();obj->setPrivate(debugger);// owns the released pointer}/* Add the initial debuggees, if any. */for(unsignedi=0;i<args.length();i++){Rooted<GlobalObject*>debuggee(cx,&args[i].toObject().as<ProxyObject>().private_().toObject().global());if(!debugger->addDebuggeeGlobal(cx,debuggee))returnfalse;}args.rval().setObject(*obj);returntrue;}boolDebugger::addDebuggeeGlobal(JSContext*cx,Handle<GlobalObject*>global){if(debuggees.has(global))returntrue;// Callers should generally be unable to get a reference to a debugger-// invisible global in order to pass it to addDebuggee. But this is possible// with certain testing aides we expose in the shell, so just make addDebuggee// throw in that case.JSCompartment*debuggeeCompartment=global->compartment();if(debuggeeCompartment->creationOptions().invisibleToDebugger()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_CANT_DEBUG_GLOBAL);returnfalse;}/* * Check for cycles. If global's compartment is reachable from this * Debugger object's compartment by following debuggee-to-debugger links, * then adding global would create a cycle. (Typically nobody is debugging * the debugger, in which case we zip through this code without looping.) */Vector<JSCompartment*>visited(cx);if(!visited.append(object->compartment()))returnfalse;for(size_ti=0;i<visited.length();i++){JSCompartment*c=visited[i];if(c==debuggeeCompartment){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_LOOP);returnfalse;}/* * Find all compartments containing debuggers debugging c's global * object. Add those compartments to visited. */if(c->isDebuggee()){GlobalObject::DebuggerVector*v=c->maybeGlobal()->getDebuggers();for(autop=v->begin();p!=v->end();p++){JSCompartment*next=(*p)->object->compartment();if(Find(visited,next)==visited.end()&&!visited.append(next))returnfalse;}}}/* * For global to become this js::Debugger's debuggee: * * 1. this js::Debugger must be in global->getDebuggers(), * 2. global must be in this->debuggees, * 3. it must be in zone->getDebuggers(), * 4. the debuggee's zone must be in this->debuggeeZones, * 5. if we are tracking allocations, the SavedStacksMetadataBuilder must be * installed for this compartment, and * 6. JSCompartment::isDebuggee()'s bit must be set. * * All six indications must be kept consistent. */AutoCompartmentac(cx,global);Zone*zone=global->zone();// (1)auto*globalDebuggers=GlobalObject::getOrCreateDebuggers(cx,global);if(!globalDebuggers)returnfalse;if(!globalDebuggers->append(this)){ReportOutOfMemory(cx);returnfalse;}autoglobalDebuggersGuard=MakeScopeExit([&]{globalDebuggers->popBack();});// (2)if(!debuggees.put(global)){ReportOutOfMemory(cx);returnfalse;}autodebuggeesGuard=MakeScopeExit([&]{debuggees.remove(global);});booladdingZoneRelation=!debuggeeZones.has(zone);// (3)auto*zoneDebuggers=zone->getOrCreateDebuggers(cx);if(!zoneDebuggers)returnfalse;if(addingZoneRelation&&!zoneDebuggers->append(this)){ReportOutOfMemory(cx);returnfalse;}autozoneDebuggersGuard=MakeScopeExit([&]{if(addingZoneRelation)zoneDebuggers->popBack();});// (4)if(addingZoneRelation&&!debuggeeZones.put(zone)){ReportOutOfMemory(cx);returnfalse;}autodebuggeeZonesGuard=MakeScopeExit([&]{if(addingZoneRelation)debuggeeZones.remove(zone);});// (5)if(trackingAllocationSites&&enabled&&!Debugger::addAllocationsTracking(cx,global))returnfalse;autoallocationsTrackingGuard=MakeScopeExit([&]{if(trackingAllocationSites&&enabled)Debugger::removeAllocationsTracking(*global);});// (6)AutoRestoreCompartmentDebugModedebugModeGuard(debuggeeCompartment);debuggeeCompartment->setIsDebuggee();debuggeeCompartment->updateDebuggerObservesAsmJS();debuggeeCompartment->updateDebuggerObservesBinarySource();debuggeeCompartment->updateDebuggerObservesCoverage();if(observesAllExecution()&&!ensureExecutionObservabilityOfCompartment(cx,debuggeeCompartment))returnfalse;globalDebuggersGuard.release();debuggeesGuard.release();zoneDebuggersGuard.release();debuggeeZonesGuard.release();allocationsTrackingGuard.release();debugModeGuard.release();returntrue;}voidDebugger::recomputeDebuggeeZoneSet(){AutoEnterOOMUnsafeRegionoomUnsafe;debuggeeZones.clear();for(autorange=debuggees.all();!range.empty();range.popFront()){if(!debuggeeZones.put(range.front().unbarrieredGet()->zone()))oomUnsafe.crash("Debugger::removeDebuggeeGlobal");}}template<typenameT>staticT*findDebuggerInVector(Debugger*dbg,Vector<T,0,js::SystemAllocPolicy>*vec){T*p;for(p=vec->begin();p!=vec->end();p++){if(*p==dbg)break;}MOZ_ASSERT(p!=vec->end());returnp;}voidDebugger::removeDebuggeeGlobal(FreeOp*fop,GlobalObject*global,WeakGlobalObjectSet::Enum*debugEnum){/* * The caller might have found global by enumerating this->debuggees; if * so, use HashSet::Enum::removeFront rather than HashSet::remove below, * to avoid invalidating the live enumerator. */MOZ_ASSERT(debuggees.has(global));MOZ_ASSERT(debuggeeZones.has(global->zone()));MOZ_ASSERT_IF(debugEnum,debugEnum->front().unbarrieredGet()==global);/* * FIXME Debugger::slowPathOnLeaveFrame needs to kill all Debugger.Frame * objects referring to a particular JS stack frame. This is hard if * Debugger objects that are no longer debugging the relevant global might * have live Frame objects. So we take the easy way out and kill them here. * This is a bug, since it's observable and contrary to the spec. One * possible fix would be to put such objects into a compartment-wide bag * which slowPathOnLeaveFrame would have to examine. */for(FrameMap::Enume(frames);!e.empty();e.popFront()){AbstractFramePtrframe=e.front().key();NativeObject*frameobj=e.front().value();if(frame.global()==global){DebuggerFrame_freeScriptFrameIterData(fop,frameobj);DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop,frame,frameobj);e.removeFront();}}auto*globalDebuggersVector=global->getDebuggers();auto*zoneDebuggersVector=global->zone()->getDebuggers();/* * The relation must be removed from up to three places: * globalDebuggersVector and debuggees for sure, and possibly the * compartment's debuggee set. * * The debuggee zone set is recomputed on demand. This avoids refcounting * and in practice we have relatively few debuggees that tend to all be in * the same zone. If after recomputing the debuggee zone set, this global's * zone is not in the set, then we must remove ourselves from the zone's * vector of observing debuggers. */globalDebuggersVector->erase(findDebuggerInVector(this,globalDebuggersVector));if(debugEnum)debugEnum->removeFront();elsedebuggees.remove(global);recomputeDebuggeeZoneSet();if(!debuggeeZones.has(global->zone()))zoneDebuggersVector->erase(findDebuggerInVector(this,zoneDebuggersVector));/* Remove all breakpoints for the debuggee. */Breakpoint*nextbp;for(Breakpoint*bp=firstBreakpoint();bp;bp=nextbp){nextbp=bp->nextInDebugger();switch(bp->site->type()){caseBreakpointSite::Type::JS:if(bp->site->asJS()->script->compartment()==global->compartment())bp->destroy(fop);break;caseBreakpointSite::Type::Wasm:if(bp->asWasm()->wasmInstance->compartment()==global->compartment())bp->destroy(fop);break;}}MOZ_ASSERT_IF(debuggees.empty(),!firstBreakpoint());/* * If we are tracking allocation sites, we need to remove the object * metadata callback from this global's compartment. */if(trackingAllocationSites)Debugger::removeAllocationsTracking(*global);if(global->getDebuggers()->empty()){global->compartment()->unsetIsDebuggee();}else{global->compartment()->updateDebuggerObservesAllExecution();global->compartment()->updateDebuggerObservesAsmJS();global->compartment()->updateDebuggerObservesBinarySource();global->compartment()->updateDebuggerObservesCoverage();}}staticinlineDebuggerSourceReferentGetSourceReferent(JSObject*obj);/* * A class for parsing 'findScripts' query arguments and searching for * scripts that match the criteria they represent. */classMOZ_STACK_CLASSDebugger::ScriptQuery{public:/* Construct a ScriptQuery to use matching scripts for |dbg|. */ScriptQuery(JSContext*cx,Debugger*dbg):cx(cx),debugger(dbg),iterMarker(&cx->runtime()->gc),compartments(cx->runtime()),url(cx),displayURLString(cx),hasSource(false),source(cx,AsVariant(static_cast<ScriptSourceObject*>(nullptr))),innermostForCompartment(cx->runtime()),vector(cx,ScriptVector(cx)),wasmInstanceVector(cx,WasmInstanceObjectVector(cx)){}/* * Initialize this ScriptQuery. Raise an error and return false if we * haven't enough memory. */boolinit(){if(!compartments.init()||!innermostForCompartment.init()){ReportOutOfMemory(cx);returnfalse;}returntrue;}/* * Parse the query object |query|, and prepare to match only the scripts * it specifies. */boolparseQuery(HandleObjectquery){/* * Check for a 'global' property, which limits the results to those * scripts scoped to a particular global object. */RootedValueglobal(cx);if(!GetProperty(cx,query,query,cx->names().global,&global))returnfalse;if(global.isUndefined()){if(!matchAllDebuggeeGlobals())returnfalse;}else{GlobalObject*globalObject=debugger->unwrapDebuggeeArgument(cx,global);if(!globalObject)returnfalse;/* * If the given global isn't a debuggee, just leave the set of * acceptable globals empty; we'll return no scripts. */if(debugger->debuggees.has(globalObject)){if(!matchSingleGlobal(globalObject))returnfalse;}}/* Check for a 'url' property. */if(!GetProperty(cx,query,query,cx->names().url,&url))returnfalse;if(!url.isUndefined()&&!url.isString()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,"query object's 'url' property","neither undefined nor a string");returnfalse;}/* Check for a 'source' property */RootedValuedebuggerSource(cx);if(!GetProperty(cx,query,query,cx->names().source,&debuggerSource))returnfalse;if(!debuggerSource.isUndefined()){if(!debuggerSource.isObject()||debuggerSource.toObject().getClass()!=&DebuggerSource_class){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,"query object's 'source' property","not undefined nor a Debugger.Source object");returnfalse;}Valueowner=debuggerSource.toObject().as<NativeObject>().getReservedSlot(JSSLOT_DEBUGSOURCE_OWNER);/* * The given source must have an owner. Otherwise, it's a * Debugger.Source.prototype, which would match no scripts, and is * probably a mistake. */if(!owner.isObject()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_PROTO,"Debugger.Source","Debugger.Source");returnfalse;}/* * If it does have an owner, it should match the Debugger we're * calling findScripts on. It would work fine even if it didn't, * but mixing Debugger.Sources is probably a sign of confusion. */if(&owner.toObject()!=debugger->object){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_WRONG_OWNER,"Debugger.Source");returnfalse;}hasSource=true;source=GetSourceReferent(&debuggerSource.toObject());}/* Check for a 'displayURL' property. */RootedValuedisplayURL(cx);if(!GetProperty(cx,query,query,cx->names().displayURL,&displayURL))returnfalse;if(!displayURL.isUndefined()&&!displayURL.isString()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,"query object's 'displayURL' property","neither undefined nor a string");returnfalse;}if(displayURL.isString()){displayURLString=displayURL.toString()->ensureLinear(cx);if(!displayURLString)returnfalse;}/* Check for a 'line' property. */RootedValuelineProperty(cx);if(!GetProperty(cx,query,query,cx->names().line,&lineProperty))returnfalse;if(lineProperty.isUndefined()){hasLine=false;}elseif(lineProperty.isNumber()){if(displayURL.isUndefined()&&url.isUndefined()&&!hasSource){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_QUERY_LINE_WITHOUT_URL);returnfalse;}doubledoubleLine=lineProperty.toNumber();if(doubleLine<=0||(unsignedint)doubleLine!=doubleLine){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_LINE);returnfalse;}hasLine=true;line=doubleLine;}else{JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,"query object's 'line' property","neither undefined nor an integer");returnfalse;}/* Check for an 'innermost' property. */PropertyName*innermostName=cx->names().innermost;RootedValueinnermostProperty(cx);if(!GetProperty(cx,query,query,innermostName,&innermostProperty))returnfalse;innermost=ToBoolean(innermostProperty);if(innermost){/* Technically, we need only check hasLine, but this is clearer. */if((displayURL.isUndefined()&&url.isUndefined()&&!hasSource)||!hasLine){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);returnfalse;}}returntrue;}/* Set up this ScriptQuery appropriately for a missing query argument. */boolomittedQuery(){url.setUndefined();hasLine=false;innermost=false;displayURLString=nullptr;returnmatchAllDebuggeeGlobals();}/* * Search all relevant compartments and the stack for scripts matching * this query, and append the matching scripts to |vector|. */boolfindScripts(){if(!prepareQuery()||!delazifyScripts())returnfalse;JSCompartment*singletonComp=nullptr;if(compartments.count()==1)singletonComp=compartments.all().front();/* Search each compartment for debuggee scripts. */MOZ_ASSERT(vector.empty());oom=false;IterateScripts(cx,singletonComp,this,considerScript);if(oom){ReportOutOfMemory(cx);returnfalse;}/* We cannot touch the gray bits while isHeapBusy, so do this now. */for(JSScript**i=vector.begin();i!=vector.end();++i)JS::ExposeScriptToActiveJS(*i);/* * For most queries, we just accumulate results in 'vector' as we find * them. But if this is an 'innermost' query, then we've accumulated the * results in the 'innermostForCompartment' map. In that case, we now need to * walk that map and populate 'vector'. */if(innermost){for(CompartmentToScriptMap::Ranger=innermostForCompartment.all();!r.empty();r.popFront()){JS::ExposeScriptToActiveJS(r.front().value());if(!vector.append(r.front().value())){ReportOutOfMemory(cx);returnfalse;}}}// TODOshu: Until such time that wasm modules are real ES6 modules,// unconditionally consider all wasm toplevel instance scripts.for(WeakGlobalObjectSet::Ranger=debugger->allDebuggees();!r.empty();r.popFront()){for(wasm::Instance*instance:r.front()->compartment()->wasm.instances()){consider(instance->object());if(oom){ReportOutOfMemory(cx);returnfalse;}}}returntrue;}Handle<ScriptVector>foundScripts()const{returnvector;}Handle<WasmInstanceObjectVector>foundWasmInstances()const{returnwasmInstanceVector;}private:/* The context in which we should do our work. */JSContext*cx;/* The debugger for which we conduct queries. */Debugger*debugger;/* Require the set of compartments to stay fixed while the ScriptQuery is alive. */gc::AutoEnterIterationiterMarker;typedefHashSet<JSCompartment*,DefaultHasher<JSCompartment*>,RuntimeAllocPolicy>CompartmentSet;/* A script must be in one of these compartments to match the query. */CompartmentSetcompartments;/* If this is a string, matching scripts have urls equal to it. */RootedValueurl;/* url as a C string. */JSAutoByteStringurlCString;/* If this is a string, matching scripts' sources have displayURLs equal to * it. */RootedLinearStringdisplayURLString;/* * If this is a source referent, matching scripts will have sources equal * to this instance. Ideally we'd use a Maybe here, but Maybe interacts * very badly with Rooted's LIFO invariant. */boolhasSource;Rooted<DebuggerSourceReferent>source;/* True if the query contained a 'line' property. */boolhasLine;/* The line matching scripts must cover. */unsignedintline;/* True if the query has an 'innermost' property whose value is true. */boolinnermost;typedefHashMap<JSCompartment*,JSScript*,DefaultHasher<JSCompartment*>,RuntimeAllocPolicy>CompartmentToScriptMap;/* * For 'innermost' queries, a map from compartments to the innermost script * we've seen so far in that compartment. (Template instantiation code size * explosion ho!) */CompartmentToScriptMapinnermostForCompartment;/* * Accumulate the scripts in an Rooted<ScriptVector>, instead of creating * the JS array as we go, because we mustn't allocate JS objects or GC * while we use the CellIter. */Rooted<ScriptVector>vector;/* * Like above, but for wasm modules. */Rooted<WasmInstanceObjectVector>wasmInstanceVector;/* Indicates whether OOM has occurred while matching. */booloom;booladdCompartment(JSCompartment*comp){returncompartments.put(comp);}/* Arrange for this ScriptQuery to match only scripts that run in |global|. */boolmatchSingleGlobal(GlobalObject*global){MOZ_ASSERT(compartments.count()==0);if(!addCompartment(global->compartment())){ReportOutOfMemory(cx);returnfalse;}returntrue;}/* * Arrange for this ScriptQuery to match all scripts running in debuggee * globals. */boolmatchAllDebuggeeGlobals(){MOZ_ASSERT(compartments.count()==0);/* Build our compartment set from the debugger's set of debuggee globals. */for(WeakGlobalObjectSet::Ranger=debugger->debuggees.all();!r.empty();r.popFront()){if(!addCompartment(r.front()->compartment())){ReportOutOfMemory(cx);returnfalse;}}returntrue;}/* * Given that parseQuery or omittedQuery has been called, prepare to match * scripts. Set urlCString and displayURLChars as appropriate. */boolprepareQuery(){/* Compute urlCString and displayURLChars, if a url or displayURL was * given respectively. */if(url.isString()){if(!urlCString.encodeLatin1(cx,url.toString()))returnfalse;}returntrue;}booldelazifyScripts(){// All scripts in debuggee compartments must be visible, so delazify// everything.for(autor=compartments.all();!r.empty();r.popFront()){JSCompartment*comp=r.front();if(!comp->ensureDelazifyScriptsForDebugger(cx))returnfalse;}returntrue;}staticvoidconsiderScript(JSRuntime*rt,void*data,JSScript*script){ScriptQuery*self=static_cast<ScriptQuery*>(data);self->consider(script);}/* * If |script| matches this query, append it to |vector| or place it in * |innermostForCompartment|, as appropriate. Set |oom| if an out of memory * condition occurred. */voidconsider(JSScript*script){// We check for presence of script->code() because it is possible that// the script was created and thus exposed to GC, but *not* fully// initialized from fullyInit{FromEmitter,Trivial} due to errors.if(oom||script->selfHosted()||!script->code())return;JSCompartment*compartment=script->compartment();if(!compartments.has(compartment))return;if(urlCString.ptr()){boolgotFilename=false;if(script->filename()&&strcmp(script->filename(),urlCString.ptr())==0)gotFilename=true;boolgotSourceURL=false;if(!gotFilename&&script->scriptSource()->introducerFilename()&&strcmp(script->scriptSource()->introducerFilename(),urlCString.ptr())==0){gotSourceURL=true;}if(!gotFilename&&!gotSourceURL)return;}if(hasLine){if(line<script->lineno()||script->lineno()+GetScriptLineExtent(script)<line)return;}if(displayURLString){if(!script->scriptSource()||!script->scriptSource()->hasDisplayURL())return;constchar16_t*s=script->scriptSource()->displayURL();if(CompareChars(s,js_strlen(s),displayURLString)!=0)return;}if(hasSource&&!(source.is<ScriptSourceObject*>()&&source.as<ScriptSourceObject*>()->source()==script->scriptSource())){return;}if(innermost){/* * For 'innermost' queries, we don't place scripts in |vector| right * away; we may later find another script that is nested inside this * one. Instead, we record the innermost script we've found so far * for each compartment in innermostForCompartment, and only * populate |vector| at the bottom of findScripts, when we've * traversed all the scripts. * * So: check this script against the innermost one we've found so * far (if any), as recorded in innermostForCompartment, and replace * that if it's better. */CompartmentToScriptMap::AddPtrp=innermostForCompartment.lookupForAdd(compartment);if(p){/* Is our newly found script deeper than the last one we found? */JSScript*incumbent=p->value();if(script->innermostScope()->chainLength()>incumbent->innermostScope()->chainLength()){p->value()=script;}}else{/* * This is the first matching script we've encountered for this * compartment, so it is thus the innermost such script. */if(!innermostForCompartment.add(p,compartment,script)){oom=true;return;}}}else{/* Record this matching script in the results vector. */if(!vector.append(script)){oom=true;return;}}return;}/* * If |instanceObject| matches this query, append it to |wasmInstanceVector|. * Set |oom| if an out of memory condition occurred. */voidconsider(WasmInstanceObject*instanceObject){if(oom)return;if(hasSource&&source!=AsVariant(instanceObject))return;if(!wasmInstanceVector.append(instanceObject))oom=true;}};/* static */boolDebugger::findScripts(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"findScripts",args,dbg);ScriptQueryquery(cx,dbg);if(!query.init())returnfalse;if(args.length()>=1){RootedObjectqueryObject(cx,NonNullObject(cx,args[0]));if(!queryObject||!query.parseQuery(queryObject))returnfalse;}else{if(!query.omittedQuery())returnfalse;}if(!query.findScripts())returnfalse;Handle<ScriptVector>scripts(query.foundScripts());Handle<WasmInstanceObjectVector>wasmInstances(query.foundWasmInstances());size_tresultLength=scripts.length()+wasmInstances.length();RootedArrayObjectresult(cx,NewDenseFullyAllocatedArray(cx,resultLength));if(!result)returnfalse;result->ensureDenseInitializedLength(cx,0,resultLength);for(size_ti=0;i<scripts.length();i++){JSObject*scriptObject=dbg->wrapScript(cx,scripts[i]);if(!scriptObject)returnfalse;result->setDenseElement(i,ObjectValue(*scriptObject));}size_twasmStart=scripts.length();for(size_ti=0;i<wasmInstances.length();i++){JSObject*scriptObject=dbg->wrapWasmScript(cx,wasmInstances[i]);if(!scriptObject)returnfalse;result->setDenseElement(wasmStart+i,ObjectValue(*scriptObject));}args.rval().setObject(*result);returntrue;}/* * A class for parsing 'findObjects' query arguments and searching for objects * that match the criteria they represent. */classMOZ_STACK_CLASSDebugger::ObjectQuery{public:/* Construct an ObjectQuery to use matching scripts for |dbg|. */ObjectQuery(JSContext*cx,Debugger*dbg):objects(cx),cx(cx),dbg(dbg),className(cx){}/* The vector that we are accumulating results in. */AutoObjectVectorobjects;/* * Parse the query object |query|, and prepare to match only the objects it * specifies. */boolparseQuery(HandleObjectquery){/* Check for the 'class' property */RootedValuecls(cx);if(!GetProperty(cx,query,query,cx->names().class_,&cls))returnfalse;if(!cls.isUndefined()){if(!cls.isString()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_UNEXPECTED_TYPE,"query object's 'class' property","neither undefined nor a string");returnfalse;}className=cls;}returntrue;}/* Set up this ObjectQuery appropriately for a missing query argument. */voidomittedQuery(){className.setUndefined();}/* * Traverse the heap to find all relevant objects and add them to the * provided vector. */boolfindObjects(){if(!prepareQuery())returnfalse;{/* * We can't tolerate the GC moving things around while we're * searching the heap. Check that nothing we do causes a GC. */Maybe<JS::AutoCheckCannotGC>maybeNoGC;RootedObjectdbgObj(cx,dbg->object);JS::ubi::RootListrootList(cx,maybeNoGC);if(!rootList.init(dbgObj)){ReportOutOfMemory(cx);returnfalse;}Traversaltraversal(cx,*this,maybeNoGC.ref());if(!traversal.init()){ReportOutOfMemory(cx);returnfalse;}traversal.wantNames=false;returntraversal.addStart(JS::ubi::Node(&rootList))&&traversal.traverse();}}/* * |ubi::Node::BreadthFirst| interface. */classNodeData{};typedefJS::ubi::BreadthFirst<ObjectQuery>Traversal;booloperator()(Traversal&traversal,JS::ubi::Nodeorigin,constJS::ubi::Edge&edge,NodeData*,boolfirst){if(!first)returntrue;JS::ubi::Nodereferent=edge.referent;/* * Only follow edges within our set of debuggee compartments; we don't * care about the heap's subgraphs outside of our debuggee compartments, * so we abandon the referent. Either (1) there is not a path from this * non-debuggee node back to a node in our debuggee compartments, and we * don't need to follow edges to or from this node, or (2) there does * exist some path from this non-debuggee node back to a node in our * debuggee compartments. However, if that were true, then the incoming * cross compartment edge back into a debuggee compartment is already * listed as an edge in the RootList we started traversal with, and * therefore we don't need to follow edges to or from this non-debuggee * node. */JSCompartment*comp=referent.compartment();if(comp&&!dbg->isDebuggeeUnbarriered(comp)){traversal.abandonReferent();returntrue;}/* * If the referent is an object and matches our query's restrictions, * add it to the vector accumulating results. Skip objects that should * never be exposed to JS, like EnvironmentObjects and internal * functions. */if(!referent.is<JSObject>()||referent.exposeToJS().isUndefined())returntrue;JSObject*obj=referent.as<JSObject>();if(!className.isUndefined()){constchar*objClassName=obj->getClass()->name;if(strcmp(objClassName,classNameCString.ptr())!=0)returntrue;}returnobjects.append(obj);}private:/* The context in which we should do our work. */JSContext*cx;/* The debugger for which we conduct queries. */Debugger*dbg;/* * If this is non-null, matching objects will have a class whose name is * this property. */RootedValueclassName;/* The className member, as a C string. */JSAutoByteStringclassNameCString;/* * Given that either omittedQuery or parseQuery has been called, prepare the * query for matching objects. */boolprepareQuery(){if(className.isString()){if(!classNameCString.encodeLatin1(cx,className.toString()))returnfalse;}returntrue;}};boolDebugger::findObjects(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"findObjects",args,dbg);ObjectQueryquery(cx,dbg);if(args.length()>=1){RootedObjectqueryObject(cx,NonNullObject(cx,args[0]));if(!queryObject||!query.parseQuery(queryObject))returnfalse;}else{query.omittedQuery();}if(!query.findObjects())returnfalse;size_tlength=query.objects.length();RootedArrayObjectresult(cx,NewDenseFullyAllocatedArray(cx,length));if(!result)returnfalse;result->ensureDenseInitializedLength(cx,0,length);for(size_ti=0;i<length;i++){RootedValuedebuggeeVal(cx,ObjectValue(*query.objects[i]));if(!dbg->wrapDebuggeeValue(cx,&debuggeeVal))returnfalse;result->setDenseElement(i,debuggeeVal);}args.rval().setObject(*result);returntrue;}/* static */boolDebugger::findAllGlobals(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"findAllGlobals",args,dbg);AutoObjectVectorglobals(cx);{// Accumulate the list of globals before wrapping them, because// wrapping can GC and collect compartments from under us, while// iterating.JS::AutoCheckCannotGCnogc;for(CompartmentsIterc(cx->runtime(),SkipAtoms);!c.done();c.next()){if(c->creationOptions().invisibleToDebugger())continue;c->scheduledForDestruction=false;GlobalObject*global=c->maybeGlobal();if(cx->runtime()->isSelfHostingGlobal(global))continue;if(global){/* * We pulled |global| out of nowhere, so it's possible that it was * marked gray by XPConnect. Since we're now exposing it to JS code, * we need to mark it black. */JS::ExposeObjectToActiveJS(global);if(!globals.append(global))returnfalse;}}}RootedObjectresult(cx,NewDenseEmptyArray(cx));if(!result)returnfalse;for(size_ti=0;i<globals.length();i++){RootedValueglobalValue(cx,ObjectValue(*globals[i]));if(!dbg->wrapDebuggeeValue(cx,&globalValue))returnfalse;if(!NewbornArrayPush(cx,result,globalValue))returnfalse;}args.rval().setObject(*result);returntrue;}/* static */boolDebugger::makeGlobalObjectReference(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"makeGlobalObjectReference",args,dbg);if(!args.requireAtLeast(cx,"Debugger.makeGlobalObjectReference",1))returnfalse;Rooted<GlobalObject*>global(cx,dbg->unwrapDebuggeeArgument(cx,args[0]));if(!global)returnfalse;// If we create a D.O referring to a global in an invisible compartment,// then from it we can reach function objects, scripts, environments, etc.,// none of which we're ever supposed to see.JSCompartment*globalCompartment=global->compartment();if(globalCompartment->creationOptions().invisibleToDebugger()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_INVISIBLE_COMPARTMENT);returnfalse;}args.rval().setObject(*global);returndbg->wrapDebuggeeValue(cx,args.rval());}#ifdef JS_TRACE_LOGGINGstaticboolDefineProperty(JSContext*cx,HandleObjectobj,HandleIdid,constchar*value,size_tn){JSString*text=JS_NewStringCopyN(cx,value,n);if(!text)returnfalse;RootedValuestr(cx,StringValue(text));returnJS_DefinePropertyById(cx,obj,id,str,JSPROP_ENUMERATE);}# ifdef NIGHTLY_BUILDboolDebugger::setupTraceLogger(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"setupTraceLogger",args,dbg);if(!args.requireAtLeast(cx,"Debugger.setupTraceLogger",1))returnfalse;RootedObjectobj(cx,ToObject(cx,args[0]));if(!obj)returnfalse;AutoIdVectorids(cx);if(!GetPropertyKeys(cx,obj,JSITER_OWNONLY,&ids))returnfalse;if(ids.length()==0){args.rval().setBoolean(true);returntrue;}Vector<uint32_t>textIds(cx);if(!textIds.reserve(ids.length()))returnfalse;Vector<bool>values(cx);if(!values.reserve(ids.length()))returnfalse;for(size_ti=0;i<ids.length();i++){if(!JSID_IS_STRING(ids[i])){args.rval().setBoolean(false);returntrue;}JSString*id=JSID_TO_STRING(ids[i]);JSLinearString*linear=id->ensureLinear(cx);if(!linear)returnfalse;uint32_ttextId=TLStringToTextId(linear);if(!TLTextIdIsTogglable(textId)){args.rval().setBoolean(false);returntrue;}RootedValuev(cx);if(!GetProperty(cx,obj,obj,ids[i],&v))returnfalse;textIds.infallibleAppend(textId);values.infallibleAppend(ToBoolean(v));}MOZ_ASSERT(ids.length()==textIds.length());MOZ_ASSERT(textIds.length()==values.length());for(size_ti=0;i<textIds.length();i++){if(values[i])TraceLogEnableTextId(cx,textIds[i]);elseTraceLogDisableTextId(cx,textIds[i]);}args.rval().setBoolean(true);returntrue;}boolDebugger::drainTraceLogger(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"drainTraceLogger",args,dbg);TraceLoggerThread*logger=TraceLoggerForCurrentThread(cx);boollostEvents=logger->lostEvents(dbg->traceLoggerLastDrainedIteration,dbg->traceLoggerLastDrainedSize);size_tnumEvents;EventEntry*events=logger->getEventsStartingAt(&dbg->traceLoggerLastDrainedIteration,&dbg->traceLoggerLastDrainedSize,&numEvents);RootedObjectarray(cx,NewDenseEmptyArray(cx));if(!array)returnfalse;JSAtom*dataAtom=Atomize(cx,"data",strlen("data"));if(!dataAtom)returnfalse;RootedIddataId(cx,AtomToId(dataAtom));/* Add all events to the array. */uint32_tindex=0;for(EventEntry*eventItem=events;eventItem<events+numEvents;eventItem++,index++){RootedObjectitem(cx,NewObjectWithGivenProto(cx,&PlainObject::class_,nullptr));if(!item)returnfalse;constchar*eventText=logger->eventText(eventItem->textId);if(!DefineProperty(cx,item,dataId,eventText,strlen(eventText)))returnfalse;RootedValueobj(cx,ObjectValue(*item));if(!JS_DefineElement(cx,array,index,obj,JSPROP_ENUMERATE))returnfalse;}/* Add "lostEvents" indicating if there are events that were lost. */RootedValuelost(cx,BooleanValue(lostEvents));if(!JS_DefineProperty(cx,array,"lostEvents",lost,JSPROP_ENUMERATE))returnfalse;args.rval().setObject(*array);returntrue;}# endif // NIGHTLY_BUILDboolDebugger::setupTraceLoggerScriptCalls(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"setupTraceLoggerScriptCalls",args,dbg);if(!args.requireAtLeast(cx,"Debugger.setupTraceLoggerScriptCalls",0))returnfalse;TraceLogEnableTextId(cx,TraceLogger_Scripts);TraceLogEnableTextId(cx,TraceLogger_InlinedScripts);TraceLogDisableTextId(cx,TraceLogger_AnnotateScripts);args.rval().setBoolean(true);returntrue;}boolDebugger::startTraceLogger(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"startTraceLogger",args,dbg);if(!args.requireAtLeast(cx,"Debugger.startTraceLogger",0))returnfalse;TraceLoggerThread*logger=TraceLoggerForCurrentThread(cx);if(!TraceLoggerEnable(logger,cx))returnfalse;args.rval().setUndefined();returntrue;}boolDebugger::endTraceLogger(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"endTraceLogger",args,dbg);if(!args.requireAtLeast(cx,"Debugger.endTraceLogger",0))returnfalse;TraceLoggerThread*logger=TraceLoggerForCurrentThread(cx);TraceLoggerDisable(logger);args.rval().setUndefined();returntrue;}boolDebugger::drainTraceLoggerScriptCalls(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"drainTraceLoggerScriptCalls",args,dbg);TraceLoggerThread*logger=TraceLoggerForCurrentThread(cx);boollostEvents=logger->lostEvents(dbg->traceLoggerScriptedCallsLastDrainedIteration,dbg->traceLoggerScriptedCallsLastDrainedSize);size_tnumEvents;EventEntry*events=logger->getEventsStartingAt(&dbg->traceLoggerScriptedCallsLastDrainedIteration,&dbg->traceLoggerScriptedCallsLastDrainedSize,&numEvents);RootedObjectarray(cx,NewDenseEmptyArray(cx));if(!array)returnfalse;JSAtom*logTypeAtom=Atomize(cx,"logType",strlen("logType"));if(!logTypeAtom)returnfalse;RootedIdfileNameId(cx,AtomToId(cx->names().fileName));RootedIdlineNumberId(cx,AtomToId(cx->names().lineNumber));RootedIdcolumnNumberId(cx,AtomToId(cx->names().columnNumber));RootedIdlogTypeId(cx,AtomToId(logTypeAtom));/* Add all events to the array. */uint32_tindex=0;for(EventEntry*eventItem=events;eventItem<events+numEvents;eventItem++){RootedObjectitem(cx,NewObjectWithGivenProto(cx,&PlainObject::class_,nullptr));if(!item)returnfalse;// Filter out internal time.uint32_ttextId=eventItem->textId;if(textId==TraceLogger_Internal){eventItem++;MOZ_ASSERT(eventItem->textId==TraceLogger_Stop);continue;}if(textId!=TraceLogger_Stop&&!logger->textIdIsScriptEvent(textId))continue;constchar*type=(textId==TraceLogger_Stop)?"Stop":"Script";if(!DefineProperty(cx,item,logTypeId,type,strlen(type)))returnfalse;if(textId!=TraceLogger_Stop){constchar*filename;constchar*lineno;constchar*colno;size_tfilename_len,lineno_len,colno_len;logger->extractScriptDetails(textId,&filename,&filename_len,&lineno,&lineno_len,&colno,&colno_len);if(!DefineProperty(cx,item,fileNameId,filename,filename_len))returnfalse;if(!DefineProperty(cx,item,lineNumberId,lineno,lineno_len))returnfalse;if(!DefineProperty(cx,item,columnNumberId,colno,colno_len))returnfalse;}RootedValueobj(cx,ObjectValue(*item));if(!JS_DefineElement(cx,array,index,obj,JSPROP_ENUMERATE))returnfalse;index++;}/* Add "lostEvents" indicating if there are events that were lost. */RootedValuelost(cx,BooleanValue(lostEvents));if(!JS_DefineProperty(cx,array,"lostEvents",lost,JSPROP_ENUMERATE))returnfalse;args.rval().setObject(*array);returntrue;}#endifboolDebugger::isCompilableUnit(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);if(!args.requireAtLeast(cx,"Debugger.isCompilableUnit",1))returnfalse;if(!args[0].isString()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_EXPECTED_TYPE,"Debugger.isCompilableUnit","string",InformalValueTypeName(args[0]));returnfalse;}JSString*str=args[0].toString();size_tlength=GetStringLength(str);AutoStableStringCharschars(cx);if(!chars.initTwoByte(cx,str))returnfalse;boolresult=true;CompileOptionsoptions(cx);frontend::UsedNameTrackerusedNames(cx);if(!usedNames.init())returnfalse;frontend::Parser<frontend::FullParseHandler,char16_t>parser(cx,cx->tempLifoAlloc(),options,chars.twoByteChars(),length,/* foldConstants = */true,usedNames,nullptr,nullptr);JS::WarningReporterolder=JS::SetWarningReporter(cx,nullptr);if(!parser.checkOptions()||!parser.parse()){// We ran into an error. If it was because we ran out of memory we report// it in the usual way.if(cx->isThrowingOutOfMemory()){JS::SetWarningReporter(cx,older);returnfalse;}// If it was because we ran out of source, we return false so our caller// knows to try to collect more [source].if(parser.isUnexpectedEOF())result=false;cx->clearPendingException();}JS::SetWarningReporter(cx,older);args.rval().setBoolean(result);returntrue;}boolDebugger::adoptDebuggeeValue(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER(cx,argc,vp,"adoptDebuggeeValue",args,dbg);if(!args.requireAtLeast(cx,"Debugger.adoptDebuggeeValue",1))returnfalse;RootedValuev(cx,args[0]);if(v.isObject()){RootedObjectobj(cx,&v.toObject());NativeObject*ndobj=ToNativeDebuggerObject(cx,&obj);if(!ndobj){returnfalse;}obj.set(static_cast<JSObject*>(ndobj->getPrivate()));v=ObjectValue(*obj);if(!dbg->wrapDebuggeeValue(cx,&v)){returnfalse;}}args.rval().set(v);returntrue;}constJSPropertySpecDebugger::properties[]={JS_PSGS("enabled",Debugger::getEnabled,Debugger::setEnabled,0),JS_PSGS("onDebuggerStatement",Debugger::getOnDebuggerStatement,Debugger::setOnDebuggerStatement,0),JS_PSGS("onExceptionUnwind",Debugger::getOnExceptionUnwind,Debugger::setOnExceptionUnwind,0),JS_PSGS("onNewScript",Debugger::getOnNewScript,Debugger::setOnNewScript,0),JS_PSGS("onNewPromise",Debugger::getOnNewPromise,Debugger::setOnNewPromise,0),JS_PSGS("onPromiseSettled",Debugger::getOnPromiseSettled,Debugger::setOnPromiseSettled,0),JS_PSGS("onEnterFrame",Debugger::getOnEnterFrame,Debugger::setOnEnterFrame,0),JS_PSGS("onNewGlobalObject",Debugger::getOnNewGlobalObject,Debugger::setOnNewGlobalObject,0),JS_PSGS("uncaughtExceptionHook",Debugger::getUncaughtExceptionHook,Debugger::setUncaughtExceptionHook,0),JS_PSGS("allowUnobservedAsmJS",Debugger::getAllowUnobservedAsmJS,Debugger::setAllowUnobservedAsmJS,0),JS_PSGS("allowWasmBinarySource",Debugger::getAllowWasmBinarySource,Debugger::setAllowWasmBinarySource,0),JS_PSGS("collectCoverageInfo",Debugger::getCollectCoverageInfo,Debugger::setCollectCoverageInfo,0),JS_PSG("memory",Debugger::getMemory,0),JS_PS_END};constJSFunctionSpecDebugger::methods[]={JS_FN("addDebuggee",Debugger::addDebuggee,1,0),JS_FN("addAllGlobalsAsDebuggees",Debugger::addAllGlobalsAsDebuggees,0,0),JS_FN("removeDebuggee",Debugger::removeDebuggee,1,0),JS_FN("removeAllDebuggees",Debugger::removeAllDebuggees,0,0),JS_FN("hasDebuggee",Debugger::hasDebuggee,1,0),JS_FN("getDebuggees",Debugger::getDebuggees,0,0),JS_FN("getNewestFrame",Debugger::getNewestFrame,0,0),JS_FN("clearAllBreakpoints",Debugger::clearAllBreakpoints,0,0),JS_FN("findScripts",Debugger::findScripts,1,0),JS_FN("findObjects",Debugger::findObjects,1,0),JS_FN("findAllGlobals",Debugger::findAllGlobals,0,0),JS_FN("makeGlobalObjectReference",Debugger::makeGlobalObjectReference,1,0),#ifdef JS_TRACE_LOGGINGJS_FN("setupTraceLoggerScriptCalls",Debugger::setupTraceLoggerScriptCalls,0,0),JS_FN("drainTraceLoggerScriptCalls",Debugger::drainTraceLoggerScriptCalls,0,0),JS_FN("startTraceLogger",Debugger::startTraceLogger,0,0),JS_FN("endTraceLogger",Debugger::endTraceLogger,0,0),# ifdef NIGHTLY_BUILDJS_FN("setupTraceLogger",Debugger::setupTraceLogger,1,0),JS_FN("drainTraceLogger",Debugger::drainTraceLogger,0,0),# endif#endifJS_FN("adoptDebuggeeValue",Debugger::adoptDebuggeeValue,1,0),JS_FS_END};constJSFunctionSpecDebugger::static_methods[]{JS_FN("isCompilableUnit",Debugger::isCompilableUnit,1,0),JS_FS_END};/*** Debugger.Script *****************************************************************************/// Get the Debugger.Script referent as bare Cell. This should only be used for// GC operations like tracing. Please use GetScriptReferent below.staticinlinegc::Cell*GetScriptReferentCell(JSObject*obj){MOZ_ASSERT(obj->getClass()==&DebuggerScript_class);returnstatic_cast<gc::Cell*>(obj->as<NativeObject>().getPrivate());}staticinlineDebuggerScriptReferentGetScriptReferent(JSObject*obj){MOZ_ASSERT(obj->getClass()==&DebuggerScript_class);if(gc::Cell*cell=GetScriptReferentCell(obj)){if(cell->getTraceKind()==JS::TraceKind::Script)returnAsVariant(static_cast<JSScript*>(cell));MOZ_ASSERT(cell->getTraceKind()==JS::TraceKind::Object);returnAsVariant(&static_cast<NativeObject*>(cell)->as<WasmInstanceObject>());}returnAsVariant(static_cast<JSScript*>(nullptr));}voidDebuggerScript_trace(JSTracer*trc,JSObject*obj){/* This comes from a private pointer, so no barrier needed. */gc::Cell*cell=GetScriptReferentCell(obj);if(cell){if(cell->getTraceKind()==JS::TraceKind::Script){JSScript*script=static_cast<JSScript*>(cell);TraceManuallyBarrieredCrossCompartmentEdge(trc,obj,&script,"Debugger.Script script referent");obj->as<NativeObject>().setPrivateUnbarriered(script);}else{JSObject*wasm=static_cast<JSObject*>(cell);TraceManuallyBarrieredCrossCompartmentEdge(trc,obj,&wasm,"Debugger.Script wasm referent");MOZ_ASSERT(wasm->is<WasmInstanceObject>());obj->as<NativeObject>().setPrivateUnbarriered(wasm);}}}classDebuggerScriptSetPrivateMatcher{NativeObject*obj_;public:explicitDebuggerScriptSetPrivateMatcher(NativeObject*obj):obj_(obj){}usingReturnType=void;ReturnTypematch(HandleScriptscript){obj_->setPrivateGCThing(script);}ReturnTypematch(Handle<WasmInstanceObject*>instance){obj_->setPrivateGCThing(instance);}};NativeObject*Debugger::newDebuggerScript(JSContext*cx,Handle<DebuggerScriptReferent>referent){assertSameCompartment(cx,object.get());RootedObjectproto(cx,&object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject());MOZ_ASSERT(proto);NativeObject*scriptobj=NewNativeObjectWithGivenProto(cx,&DebuggerScript_class,proto,TenuredObject);if(!scriptobj)returnnullptr;scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER,ObjectValue(*object));DebuggerScriptSetPrivateMatchermatcher(scriptobj);referent.match(matcher);returnscriptobj;}template<typenameReferentVariant,typenameReferent,typenameMap>JSObject*Debugger::wrapVariantReferent(JSContext*cx,Map&map,Handle<CrossCompartmentKey>key,Handle<ReferentVariant>referent){assertSameCompartment(cx,object);Handle<Referent>untaggedReferent=referent.templateas<Referent>();MOZ_ASSERT(cx->compartment()!=untaggedReferent->compartment());DependentAddPtr<Map>p(cx,map,untaggedReferent);if(!p){NativeObject*wrapper=newVariantWrapper(cx,referent);if(!wrapper)returnnullptr;if(!p.add(cx,map,untaggedReferent,wrapper)){NukeDebuggerWrapper(wrapper);returnnullptr;}if(!object->compartment()->putWrapper(cx,key,ObjectValue(*wrapper))){NukeDebuggerWrapper(wrapper);map.remove(untaggedReferent);ReportOutOfMemory(cx);returnnullptr;}}returnp->value();}JSObject*Debugger::wrapVariantReferent(JSContext*cx,Handle<DebuggerScriptReferent>referent){JSObject*obj;if(referent.is<JSScript*>()){Handle<JSScript*>untaggedReferent=referent.templateas<JSScript*>();Rooted<CrossCompartmentKey>key(cx,CrossCompartmentKey(object,untaggedReferent));obj=wrapVariantReferent<DebuggerScriptReferent,JSScript*,ScriptWeakMap>(cx,scripts,key,referent);}else{Handle<WasmInstanceObject*>untaggedReferent=referent.templateas<WasmInstanceObject*>();Rooted<CrossCompartmentKey>key(cx,CrossCompartmentKey(object,untaggedReferent,CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmScript));obj=wrapVariantReferent<DebuggerScriptReferent,WasmInstanceObject*,WasmInstanceWeakMap>(cx,wasmInstanceScripts,key,referent);}MOZ_ASSERT_IF(obj,GetScriptReferent(obj)==referent);returnobj;}JSObject*Debugger::wrapScript(JSContext*cx,HandleScriptscript){Rooted<DebuggerScriptReferent>referent(cx,script.get());returnwrapVariantReferent(cx,referent);}JSObject*Debugger::wrapWasmScript(JSContext*cx,Handle<WasmInstanceObject*>wasmInstance){Rooted<DebuggerScriptReferent>referent(cx,wasmInstance.get());returnwrapVariantReferent(cx,referent);}staticJSObject*DebuggerScript_check(JSContext*cx,constValue&v,constchar*fnname){JSObject*thisobj=NonNullObject(cx,v);if(!thisobj)returnnullptr;if(thisobj->getClass()!=&DebuggerScript_class){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Script",fnname,thisobj->getClass()->name);returnnullptr;}/* * Check for Debugger.Script.prototype, which is of class DebuggerScript_class * but whose script is null. */if(!GetScriptReferentCell(thisobj)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Script",fnname,"prototype object");returnnullptr;}returnthisobj;}template<typenameReferentT>staticJSObject*DebuggerScript_checkThis(JSContext*cx,constCallArgs&args,constchar*fnname,constchar*refname){JSObject*thisobj=DebuggerScript_check(cx,args.thisv(),fnname);if(!thisobj)returnnullptr;if(!GetScriptReferent(thisobj).is<ReferentT>()){ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_BAD_REFERENT,JSDVG_SEARCH_STACK,args.thisv(),nullptr,refname,nullptr);returnnullptr;}returnthisobj;}#define THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, fnname, args, obj, referent) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedObject obj(cx, DebuggerScript_check(cx, args.thisv(), fnname)); \ if (!obj) \ return false; \ Rooted<DebuggerScriptReferent> referent(cx, GetScriptReferent(obj))#define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedObject obj(cx, DebuggerScript_checkThis<JSScript*>(cx, args, fnname, \ "a JS script")); \ if (!obj) \ return false; \ RootedScript script(cx, GetScriptReferent(obj).as<JSScript*>())staticboolDebuggerScript_getDisplayName(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"(get displayName)",args,obj,script);Debugger*dbg=Debugger::fromChildJSObject(obj);JSFunction*func=script->functionNonDelazifying();JSString*name=func?func->displayAtom():nullptr;if(!name){args.rval().setUndefined();returntrue;}RootedValuenamev(cx,StringValue(name));if(!dbg->wrapDebuggeeValue(cx,&namev))returnfalse;args.rval().set(namev);returntrue;}staticboolDebuggerScript_getUrl(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"(get url)",args,obj,script);if(script->filename()){JSString*str;if(script->scriptSource()->introducerFilename())str=NewStringCopyZ<CanGC>(cx,script->scriptSource()->introducerFilename());elsestr=NewStringCopyZ<CanGC>(cx,script->filename());if(!str)returnfalse;args.rval().setString(str);}else{args.rval().setNull();}returntrue;}structDebuggerScriptGetStartLineMatcher{usingReturnType=uint32_t;ReturnTypematch(HandleScriptscript){returnuint32_t(script->lineno());}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){return1;}};staticboolDebuggerScript_getStartLine(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"(get startLine)",args,obj,referent);DebuggerScriptGetStartLineMatchermatcher;args.rval().setNumber(referent.match(matcher));returntrue;}structDebuggerScriptGetLineCountMatcher{JSContext*cx_;doubletotalLines;explicitDebuggerScriptGetLineCountMatcher(JSContext*cx):cx_(cx){}usingReturnType=bool;ReturnTypematch(HandleScriptscript){totalLines=double(GetScriptLineExtent(script));returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){uint32_tresult;if(!wasmInstance->instance().debug().totalSourceLines(cx_,&result))returnfalse;totalLines=double(result);returntrue;}};staticboolDebuggerScript_getLineCount(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"(get lineCount)",args,obj,referent);DebuggerScriptGetLineCountMatchermatcher(cx);if(!referent.match(matcher))returnfalse;args.rval().setNumber(matcher.totalLines);returntrue;}classDebuggerScriptGetSourceMatcher{JSContext*cx_;Debugger*dbg_;public:DebuggerScriptGetSourceMatcher(JSContext*cx,Debugger*dbg):cx_(cx),dbg_(dbg){}usingReturnType=JSObject*;ReturnTypematch(HandleScriptscript){RootedScriptSourcesource(cx_,&UncheckedUnwrap(script->sourceObject())->as<ScriptSourceObject>());returndbg_->wrapSource(cx_,source);}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){returndbg_->wrapWasmSource(cx_,wasmInstance);}};staticboolDebuggerScript_getSource(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"(get source)",args,obj,referent);Debugger*dbg=Debugger::fromChildJSObject(obj);DebuggerScriptGetSourceMatchermatcher(cx,dbg);RootedObjectsourceObject(cx,referent.match(matcher));if(!sourceObject)returnfalse;args.rval().setObject(*sourceObject);returntrue;}staticboolDebuggerScript_getSourceStart(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"(get sourceStart)",args,obj,script);args.rval().setNumber(uint32_t(script->sourceStart()));returntrue;}staticboolDebuggerScript_getSourceLength(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"(get sourceEnd)",args,obj,script);args.rval().setNumber(uint32_t(script->sourceEnd()-script->sourceStart()));returntrue;}staticboolDebuggerScript_getGlobal(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"(get global)",args,obj,script);Debugger*dbg=Debugger::fromChildJSObject(obj);RootedValuev(cx,ObjectValue(script->global()));if(!dbg->wrapDebuggeeValue(cx,&v))returnfalse;args.rval().set(v);returntrue;}classDebuggerScriptGetFormatMatcher{constJSAtomState&names_;public:explicitDebuggerScriptGetFormatMatcher(constJSAtomState&names):names_(names){}usingReturnType=JSAtom*;ReturnTypematch(HandleScriptscript){returnnames_.js;}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){returnnames_.wasm;}};staticboolDebuggerScript_getFormat(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"(get format)",args,obj,referent);DebuggerScriptGetFormatMatchermatcher(cx->names());args.rval().setString(referent.match(matcher));returntrue;}staticboolDebuggerScript_getChildScripts(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"getChildScripts",args,obj,script);Debugger*dbg=Debugger::fromChildJSObject(obj);RootedObjectresult(cx,NewDenseEmptyArray(cx));if(!result)returnfalse;if(script->hasObjects()){/* * script->savedCallerFun indicates that this is a direct eval script * and the calling function is stored as script->objects()->vector[0]. * It is not really a child script of this script, so skip it using * innerObjectsStart(). */ObjectArray*objects=script->objects();RootedFunctionfun(cx);RootedScriptfunScript(cx);RootedObjectobj(cx),s(cx);for(uint32_ti=0;i<objects->length;i++){obj=objects->vector[i];if(obj->is<JSFunction>()){fun=&obj->as<JSFunction>();// The inner function could be a wasm native.if(fun->isNative())continue;funScript=GetOrCreateFunctionScript(cx,fun);if(!funScript)returnfalse;s=dbg->wrapScript(cx,funScript);if(!s||!NewbornArrayPush(cx,result,ObjectValue(*s)))returnfalse;}}}args.rval().setObject(*result);returntrue;}staticboolScriptOffset(JSContext*cx,constValue&v,size_t*offsetp){doubled;size_toff;boolok=v.isNumber();if(ok){d=v.toNumber();off=size_t(d);}if(!ok||off!=d){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_OFFSET);returnfalse;}*offsetp=off;returntrue;}staticboolEnsureScriptOffsetIsValid(JSContext*cx,JSScript*script,size_toffset){if(IsValidBytecodeOffset(cx,script,offset))returntrue;JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_OFFSET);returnfalse;}namespace{classBytecodeRangeWithPosition:privateBytecodeRange{public:usingBytecodeRange::empty;usingBytecodeRange::frontPC;usingBytecodeRange::frontOpcode;usingBytecodeRange::frontOffset;BytecodeRangeWithPosition(JSContext*cx,JSScript*script):BytecodeRange(cx,script),lineno(script->lineno()),column(0),sn(script->notes()),snpc(script->code()),isEntryPoint(false),wasArtifactEntryPoint(false){if(!SN_IS_TERMINATOR(sn))snpc+=SN_DELTA(sn);updatePosition();while(frontPC()!=script->main())popFront();if(frontOpcode()!=JSOP_JUMPTARGET)isEntryPoint=true;elsewasArtifactEntryPoint=true;}voidpopFront(){BytecodeRange::popFront();if(empty())isEntryPoint=false;elseupdatePosition();// The following conditions are handling artifacts introduced by the// bytecode emitter, such that we do not add breakpoints on empty// statements of the source code of the user.if(wasArtifactEntryPoint){wasArtifactEntryPoint=false;isEntryPoint=true;}if(isEntryPoint&&frontOpcode()==JSOP_JUMPTARGET){wasArtifactEntryPoint=isEntryPoint;isEntryPoint=false;}}size_tfrontLineNumber()const{returnlineno;}size_tfrontColumnNumber()const{returncolumn;}// Entry points are restricted to bytecode offsets that have an// explicit mention in the line table. This restriction avoids a// number of failing cases caused by some instructions not having// sensible (to the user) line numbers, and it is one way to// implement the idea that the bytecode emitter should tell the// debugger exactly which offsets represent "interesting" (to the// user) places to stop.boolfrontIsEntryPoint()const{returnisEntryPoint;}private:voidupdatePosition(){/* * Determine the current line number by reading all source notes up to * and including the current offset. */jsbytecode*lastLinePC=nullptr;while(!SN_IS_TERMINATOR(sn)&&snpc<=frontPC()){SrcNoteTypetype=(SrcNoteType)SN_TYPE(sn);if(type==SRC_COLSPAN){ptrdiff_tcolspan=SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn,0));MOZ_ASSERT(ptrdiff_t(column)+colspan>=0);column+=colspan;lastLinePC=snpc;}elseif(type==SRC_SETLINE){lineno=size_t(GetSrcNoteOffset(sn,0));column=0;lastLinePC=snpc;}elseif(type==SRC_NEWLINE){lineno++;column=0;lastLinePC=snpc;}sn=SN_NEXT(sn);snpc+=SN_DELTA(sn);}isEntryPoint=lastLinePC==frontPC();}size_tlineno;size_tcolumn;jssrcnote*sn;jsbytecode*snpc;boolisEntryPoint;boolwasArtifactEntryPoint;};/* * FlowGraphSummary::populate(cx, script) computes a summary of script's * control flow graph used by DebuggerScript_{getAllOffsets,getLineOffsets}. * * An instruction on a given line is an entry point for that line if it can be * reached from (an instruction on) a different line. We distinguish between the * following cases: * - hasNoEdges: * The instruction cannot be reached, so the instruction is not an entry * point for the line it is on. * - hasSingleEdge: * - hasMultipleEdgesFromSingleLine: * The instruction can be reached from a single line. If this line is * different from the line the instruction is on, the instruction is an * entry point for that line. * - hasMultipleEdgesFromMultipleLines: * The instruction can be reached from multiple lines. At least one of * these lines is guaranteed to be different from the line the instruction * is on, so the instruction is an entry point for that line. * * Similarly, an instruction on a given position (line/column pair) is an * entry point for that position if it can be reached from (an instruction on) a * different position. Again, we distinguish between the following cases: * - hasNoEdges: * The instruction cannot be reached, so the instruction is not an entry * point for the position it is on. * - hasSingleEdge: * The instruction can be reached from a single position. If this line is * different from the position the instruction is on, the instruction is * an entry point for that position. * - hasMultipleEdgesFromSingleLine: * - hasMultipleEdgesFromMultipleLines: * The instruction can be reached from multiple positions. At least one * of these positions is guaranteed to be different from the position the * instruction is on, so the instruction is an entry point for that * position. */classFlowGraphSummary{public:classEntry{public:staticEntrycreateWithNoEdges(){returnEntry(SIZE_MAX,0);}staticEntrycreateWithSingleEdge(size_tlineno,size_tcolumn){returnEntry(lineno,column);}staticEntrycreateWithMultipleEdgesFromSingleLine(size_tlineno){returnEntry(lineno,SIZE_MAX);}staticEntrycreateWithMultipleEdgesFromMultipleLines(){returnEntry(SIZE_MAX,SIZE_MAX);}Entry():lineno_(SIZE_MAX),column_(0){}boolhasNoEdges()const{returnlineno_==SIZE_MAX&&column_!=SIZE_MAX;}boolhasSingleEdge()const{returnlineno_!=SIZE_MAX&&column_!=SIZE_MAX;}boolhasMultipleEdgesFromSingleLine()const{returnlineno_!=SIZE_MAX&&column_==SIZE_MAX;}boolhasMultipleEdgesFromMultipleLines()const{returnlineno_==SIZE_MAX&&column_==SIZE_MAX;}booloperator==(constEntry&other)const{returnlineno_==other.lineno_&&column_==other.column_;}booloperator!=(constEntry&other)const{returnlineno_!=other.lineno_||column_!=other.column_;}size_tlineno()const{returnlineno_;}size_tcolumn()const{returncolumn_;}private:Entry(size_tlineno,size_tcolumn):lineno_(lineno),column_(column){}size_tlineno_;size_tcolumn_;};explicitFlowGraphSummary(JSContext*cx):entries_(cx){}Entry&operator[](size_tindex){returnentries_[index];}boolpopulate(JSContext*cx,JSScript*script){if(!entries_.growBy(script->length()))returnfalse;unsignedmainOffset=script->pcToOffset(script->main());entries_[mainOffset]=Entry::createWithMultipleEdgesFromMultipleLines();size_tprevLineno=script->lineno();size_tprevColumn=0;JSOpprevOp=JSOP_NOP;for(BytecodeRangeWithPositionr(cx,script);!r.empty();r.popFront()){size_tlineno=prevLineno;size_tcolumn=prevColumn;JSOpop=r.frontOpcode();if(FlowsIntoNext(prevOp))addEdge(prevLineno,prevColumn,r.frontOffset());// If we visit the branch target before we visit the// branch op itself, just reuse the previous location.// This is reasonable for the time being because this// situation can currently only arise from loop heads,// where this assumption holds.if(BytecodeIsJumpTarget(op)&&!entries_[r.frontOffset()].hasNoEdges()){lineno=entries_[r.frontOffset()].lineno();column=entries_[r.frontOffset()].column();}if(r.frontIsEntryPoint()){lineno=r.frontLineNumber();column=r.frontColumnNumber();}if(CodeSpec[op].type()==JOF_JUMP){addEdge(lineno,column,r.frontOffset()+GET_JUMP_OFFSET(r.frontPC()));}elseif(op==JSOP_TABLESWITCH){jsbytecode*pc=r.frontPC();size_toffset=r.frontOffset();ptrdiff_tstep=JUMP_OFFSET_LEN;size_tdefaultOffset=offset+GET_JUMP_OFFSET(pc);pc+=step;addEdge(lineno,column,defaultOffset);int32_tlow=GET_JUMP_OFFSET(pc);pc+=JUMP_OFFSET_LEN;intncases=GET_JUMP_OFFSET(pc)-low+1;pc+=JUMP_OFFSET_LEN;for(inti=0;i<ncases;i++){size_ttarget=offset+GET_JUMP_OFFSET(pc);addEdge(lineno,column,target);pc+=step;}}elseif(op==JSOP_TRY){// As there is no literal incoming edge into the catch block, we// make a fake one by copying the JSOP_TRY location, as-if this// was an incoming edge of the catch block. This is needed// because we only report offsets of entry points which have// valid incoming edges.JSTryNote*tn=script->trynotes()->vector;JSTryNote*tnlimit=tn+script->trynotes()->length;for(;tn<tnlimit;tn++){uint32_tstartOffset=script->mainOffset()+tn->start;if(startOffset==r.frontOffset()+1){uint32_tcatchOffset=startOffset+tn->length;if(tn->kind==JSTRY_CATCH||tn->kind==JSTRY_FINALLY)addEdge(lineno,column,catchOffset);}}}prevLineno=lineno;prevColumn=column;prevOp=op;}returntrue;}private:voidaddEdge(size_tsourceLineno,size_tsourceColumn,size_ttargetOffset){if(entries_[targetOffset].hasNoEdges())entries_[targetOffset]=Entry::createWithSingleEdge(sourceLineno,sourceColumn);elseif(entries_[targetOffset].lineno()!=sourceLineno)entries_[targetOffset]=Entry::createWithMultipleEdgesFromMultipleLines();elseif(entries_[targetOffset].column()!=sourceColumn)entries_[targetOffset]=Entry::createWithMultipleEdgesFromSingleLine(sourceLineno);}Vector<Entry>entries_;};}/* anonymous namespace */classDebuggerScriptGetOffsetLocationMatcher{JSContext*cx_;size_toffset_;MutableHandlePlainObjectresult_;public:explicitDebuggerScriptGetOffsetLocationMatcher(JSContext*cx,size_toffset,MutableHandlePlainObjectresult):cx_(cx),offset_(offset),result_(result){}usingReturnType=bool;ReturnTypematch(HandleScriptscript){if(!EnsureScriptOffsetIsValid(cx_,script,offset_))returnfalse;FlowGraphSummaryflowData(cx_);if(!flowData.populate(cx_,script))returnfalse;result_.set(NewBuiltinClassInstance<PlainObject>(cx_));if(!result_)returnfalse;BytecodeRangeWithPositionr(cx_,script);while(!r.empty()&&r.frontOffset()<offset_)r.popFront();size_toffset=r.frontOffset();boolisEntryPoint=r.frontIsEntryPoint();// Line numbers are only correctly defined on entry points. Thus looks// either for the next valid offset in the flowData, being the last entry// point flowing into the current offset, or for the next valid entry point.while(!r.frontIsEntryPoint()&&!flowData[r.frontOffset()].hasSingleEdge()){r.popFront();MOZ_ASSERT(!r.empty());}// If this is an entry point, take the line number associated with the entry// point, otherwise settle on the next instruction and take the incoming// edge position.size_tlineno;size_tcolumn;if(r.frontIsEntryPoint()){lineno=r.frontLineNumber();column=r.frontColumnNumber();}else{MOZ_ASSERT(flowData[r.frontOffset()].hasSingleEdge());lineno=flowData[r.frontOffset()].lineno();column=flowData[r.frontOffset()].column();}RootedIdid(cx_,NameToId(cx_->names().lineNumber));RootedValuevalue(cx_,NumberValue(lineno));if(!DefineProperty(cx_,result_,id,value))returnfalse;value=NumberValue(column);if(!DefineProperty(cx_,result_,cx_->names().columnNumber,value))returnfalse;// The same entry point test that is used by getAllColumnOffsets.isEntryPoint=(isEntryPoint&&!flowData[offset].hasNoEdges()&&(flowData[offset].lineno()!=r.frontLineNumber()||flowData[offset].column()!=r.frontColumnNumber()));value.setBoolean(isEntryPoint);if(!DefineProperty(cx_,result_,cx_->names().isEntryPoint,value))returnfalse;returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>instance){size_tlineno;size_tcolumn;boolfound;if(!instance->instance().debug().getOffsetLocation(cx_,offset_,&found,&lineno,&column))returnfalse;if(!found){JS_ReportErrorNumberASCII(cx_,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_OFFSET);returnfalse;}result_.set(NewBuiltinClassInstance<PlainObject>(cx_));if(!result_)returnfalse;RootedIdid(cx_,NameToId(cx_->names().lineNumber));RootedValuevalue(cx_,NumberValue(lineno));if(!DefineProperty(cx_,result_,id,value))returnfalse;value=NumberValue(column);if(!DefineProperty(cx_,result_,cx_->names().columnNumber,value))returnfalse;value.setBoolean(true);if(!DefineProperty(cx_,result_,cx_->names().isEntryPoint,value))returnfalse;returntrue;}};staticboolDebuggerScript_getOffsetLocation(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"getOffsetLocation",args,obj,referent);if(!args.requireAtLeast(cx,"Debugger.Script.getOffsetLocation",1))returnfalse;size_toffset;if(!ScriptOffset(cx,args[0],&offset))returnfalse;RootedPlainObjectresult(cx);DebuggerScriptGetOffsetLocationMatchermatcher(cx,offset,&result);if(!referent.match(matcher))returnfalse;args.rval().setObject(*result);returntrue;}staticboolDebuggerScript_getAllOffsets(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"getAllOffsets",args,obj,script);/* * First pass: determine which offsets in this script are jump targets and * which line numbers jump to them. */FlowGraphSummaryflowData(cx);if(!flowData.populate(cx,script))returnfalse;/* Second pass: build the result array. */RootedObjectresult(cx,NewDenseEmptyArray(cx));if(!result)returnfalse;for(BytecodeRangeWithPositionr(cx,script);!r.empty();r.popFront()){if(!r.frontIsEntryPoint())continue;size_toffset=r.frontOffset();size_tlineno=r.frontLineNumber();/* Make a note, if the current instruction is an entry point for the current line. */if(!flowData[offset].hasNoEdges()&&flowData[offset].lineno()!=lineno){/* Get the offsets array for this line. */RootedObjectoffsets(cx);RootedValueoffsetsv(cx);RootedIdid(cx,INT_TO_JSID(lineno));boolfound;if(!HasOwnProperty(cx,result,id,&found))returnfalse;if(found&&!GetProperty(cx,result,result,id,&offsetsv))returnfalse;if(offsetsv.isObject()){offsets=&offsetsv.toObject();}else{MOZ_ASSERT(offsetsv.isUndefined());/* * Create an empty offsets array for this line. * Store it in the result array. */RootedIdid(cx);RootedValuev(cx,NumberValue(lineno));offsets=NewDenseEmptyArray(cx);if(!offsets||!ValueToId<CanGC>(cx,v,&id)){returnfalse;}RootedValuevalue(cx,ObjectValue(*offsets));if(!DefineProperty(cx,result,id,value))returnfalse;}/* Append the current offset to the offsets array. */if(!NewbornArrayPush(cx,offsets,NumberValue(offset)))returnfalse;}}args.rval().setObject(*result);returntrue;}classDebuggerScriptGetAllColumnOffsetsMatcher{JSContext*cx_;MutableHandleObjectresult_;boolappendColumnOffsetEntry(size_tlineno,size_tcolumn,size_toffset){RootedPlainObjectentry(cx_,NewBuiltinClassInstance<PlainObject>(cx_));if(!entry)returnfalse;RootedIdid(cx_,NameToId(cx_->names().lineNumber));RootedValuevalue(cx_,NumberValue(lineno));if(!DefineProperty(cx_,entry,id,value))returnfalse;value=NumberValue(column);if(!DefineProperty(cx_,entry,cx_->names().columnNumber,value))returnfalse;id=NameToId(cx_->names().offset);value=NumberValue(offset);if(!DefineProperty(cx_,entry,id,value))returnfalse;returnNewbornArrayPush(cx_,result_,ObjectValue(*entry));}public:explicitDebuggerScriptGetAllColumnOffsetsMatcher(JSContext*cx,MutableHandleObjectresult):cx_(cx),result_(result){}usingReturnType=bool;ReturnTypematch(HandleScriptscript){/* * First pass: determine which offsets in this script are jump targets * and which positions jump to them. */FlowGraphSummaryflowData(cx_);if(!flowData.populate(cx_,script))returnfalse;/* Second pass: build the result array. */result_.set(NewDenseEmptyArray(cx_));if(!result_)returnfalse;for(BytecodeRangeWithPositionr(cx_,script);!r.empty();r.popFront()){size_tlineno=r.frontLineNumber();size_tcolumn=r.frontColumnNumber();size_toffset=r.frontOffset();/* * Make a note, if the current instruction is an entry point for * the current position. */if(r.frontIsEntryPoint()&&!flowData[offset].hasNoEdges()&&(flowData[offset].lineno()!=lineno||flowData[offset].column()!=column)){if(!appendColumnOffsetEntry(lineno,column,offset))returnfalse;}}returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>instance){Vector<wasm::ExprLoc>offsets(cx_);if(!instance->instance().debug().getAllColumnOffsets(cx_,&offsets))returnfalse;result_.set(NewDenseEmptyArray(cx_));if(!result_)returnfalse;for(uint32_ti=0;i<offsets.length();i++){size_tlineno=offsets[i].lineno;size_tcolumn=offsets[i].column;size_toffset=offsets[i].offset;if(!appendColumnOffsetEntry(lineno,column,offset))returnfalse;}returntrue;}};staticboolDebuggerScript_getAllColumnOffsets(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"getAllColumnOffsets",args,obj,referent);RootedObjectresult(cx);DebuggerScriptGetAllColumnOffsetsMatchermatcher(cx,&result);if(!referent.match(matcher))returnfalse;args.rval().setObject(*result);returntrue;}classDebuggerScriptGetLineOffsetsMatcher{JSContext*cx_;size_tlineno_;MutableHandleObjectresult_;public:explicitDebuggerScriptGetLineOffsetsMatcher(JSContext*cx,size_tlineno,MutableHandleObjectresult):cx_(cx),lineno_(lineno),result_(result){}usingReturnType=bool;ReturnTypematch(HandleScriptscript){/* * First pass: determine which offsets in this script are jump targets and * which line numbers jump to them. */FlowGraphSummaryflowData(cx_);if(!flowData.populate(cx_,script))returnfalse;result_.set(NewDenseEmptyArray(cx_));if(!result_)returnfalse;/* Second pass: build the result array. */for(BytecodeRangeWithPositionr(cx_,script);!r.empty();r.popFront()){if(!r.frontIsEntryPoint())continue;size_toffset=r.frontOffset();/* If the op at offset is an entry point, append offset to result. */if(r.frontLineNumber()==lineno_&&!flowData[offset].hasNoEdges()&&flowData[offset].lineno()!=lineno_){if(!NewbornArrayPush(cx_,result_,NumberValue(offset)))returnfalse;}}returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>instance){Vector<uint32_t>offsets(cx_);if(!instance->instance().debug().getLineOffsets(cx_,lineno_,&offsets))returnfalse;result_.set(NewDenseEmptyArray(cx_));if(!result_)returnfalse;for(uint32_ti=0;i<offsets.length();i++){if(!NewbornArrayPush(cx_,result_,NumberValue(offsets[i])))returnfalse;}returntrue;}};staticboolDebuggerScript_getLineOffsets(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"getLineOffsets",args,obj,referent);if(!args.requireAtLeast(cx,"Debugger.Script.getLineOffsets",1))returnfalse;/* Parse lineno argument. */RootedValuelinenoValue(cx,args[0]);size_tlineno;if(!ToNumber(cx,&linenoValue))returnfalse;{doubled=linenoValue.toNumber();lineno=size_t(d);if(lineno!=d){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_LINE);returnfalse;}}RootedObjectresult(cx);DebuggerScriptGetLineOffsetsMatchermatcher(cx,lineno,&result);if(!referent.match(matcher))returnfalse;args.rval().setObject(*result);returntrue;}boolDebugger::observesFrame(AbstractFramePtrframe)const{if(frame.isWasmDebugFrame())returnobservesWasm(frame.wasmInstance());returnobservesScript(frame.script());}boolDebugger::observesFrame(constFrameIter&iter)const{// Skip frames not yet fully initialized during their prologue.if(iter.isInterp()&&iter.isFunctionFrame()){constValue&thisVal=iter.interpFrame()->thisArgument();if(thisVal.isMagic()&&thisVal.whyMagic()==JS_IS_CONSTRUCTING)returnfalse;}if(iter.isWasm()){// Skip frame of wasm instances we cannot observe.if(!iter.wasmDebugEnabled())returnfalse;returnobservesWasm(iter.wasmInstance());}returnobservesScript(iter.script());}boolDebugger::observesScript(JSScript*script)const{if(!enabled)returnfalse;// Don't ever observe self-hosted scripts: the Debugger API can break// self-hosted invariants.returnobservesGlobal(&script->global())&&!script->selfHosted();}boolDebugger::observesWasm(wasm::Instance*instance)const{if(!enabled||!instance->debugEnabled())returnfalse;returnobservesGlobal(&instance->object()->global());}/* static */boolDebugger::replaceFrameGuts(JSContext*cx,AbstractFramePtrfrom,AbstractFramePtrto,ScriptFrameIter&iter){autoremoveFromDebuggerFramesOnExit=MakeScopeExit([&]{// Remove any remaining old entries on exit, as the 'from' frame will// be gone. This is only done in the failure case. On failure, the// removeToDebuggerFramesOnExit lambda below will rollback any frames// that were replaced, resulting in !frameMaps(to). On success, the// range will be empty, as all from Frame.Debugger instances will have// been removed.MOZ_ASSERT_IF(inFrameMaps(to),!inFrameMaps(from));removeFromFrameMapsAndClearBreakpointsIn(cx,from);// Rekey missingScopes to maintain Debugger.Environment identity and// forward liveScopes to point to the new frame.DebugEnvironments::forwardLiveFrame(cx,from,to);});// Forward live Debugger.Frame objects.Rooted<DebuggerFrameVector>frames(cx,DebuggerFrameVector(cx));if(!getDebuggerFrames(from,&frames)){// An OOM here means that all Debuggers' frame maps still contain// entries for 'from' and no entries for 'to'. Since the 'from' frame// will be gone, they are removed by removeFromDebuggerFramesOnExit// above.returnfalse;}// If during the loop below we hit an OOM, we must also rollback any of// the frames that were successfully replaced. For OSR frames, OOM here// means those frames will pop from the OSR trampoline, which does not// call Debugger::onLeaveFrame.autoremoveToDebuggerFramesOnExit=MakeScopeExit([&]{removeFromFrameMapsAndClearBreakpointsIn(cx,to);});for(size_ti=0;i<frames.length();i++){HandleDebuggerFrameframeobj=frames[i];Debugger*dbg=Debugger::fromChildJSObject(frameobj);// Update frame object's ScriptFrameIter::data pointer.DebuggerFrame_freeScriptFrameIterData(cx->runtime()->defaultFreeOp(),frameobj);ScriptFrameIter::Data*data=iter.copyData();if(!data){// An OOM here means that some Debuggers' frame maps may still// contain entries for 'from' and some Debuggers' frame maps may// also contain entries for 'to'. Thus both// removeFromDebuggerFramesOnExit and// removeToDebuggerFramesOnExit must both run.//// The current frameobj in question is still in its Debugger's// frame map keyed by 'from', so it will be covered by// removeFromDebuggerFramesOnExit.returnfalse;}frameobj->setPrivate(data);// Remove old frame.dbg->frames.remove(from);// Add the frame object with |to| as key.if(!dbg->frames.putNew(to,frameobj)){// This OOM is subtle. At this point, both// removeFromDebuggerFramesOnExit and removeToDebuggerFramesOnExit// must both run for the same reason given above.//// The difference is that the current frameobj is no longer in its// Debugger's frame map, so it will not be cleaned up by neither// lambda. Manually clean it up here.FreeOp*fop=cx->runtime()->defaultFreeOp();DebuggerFrame_freeScriptFrameIterData(fop,frameobj);DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop,to,frameobj);ReportOutOfMemory(cx);returnfalse;}}// All frames successfuly replaced, cancel the rollback.removeToDebuggerFramesOnExit.release();returntrue;}/* static */boolDebugger::inFrameMaps(AbstractFramePtrframe){boolfoundAny=false;forEachDebuggerFrame(frame,[&](NativeObject*frameobj){foundAny=true;});returnfoundAny;}/* static */voidDebugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext*cx,AbstractFramePtrframe){forEachDebuggerFrame(frame,[&](NativeObject*frameobj){Debugger*dbg=Debugger::fromChildJSObject(frameobj);FreeOp*fop=cx->runtime()->defaultFreeOp();DebuggerFrame_freeScriptFrameIterData(fop,frameobj);DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop,frame,frameobj);dbg->frames.remove(frame);});/* * If this is an eval frame, then from the debugger's perspective the * script is about to be destroyed. Remove any breakpoints in it. */if(frame.isEvalFrame()){RootedScriptscript(cx,frame.script());script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(),nullptr,nullptr);}}/* static */boolDebugger::handleBaselineOsr(JSContext*cx,InterpreterFrame*from,jit::BaselineFrame*to){ScriptFrameIteriter(cx);MOZ_ASSERT(iter.abstractFramePtr()==to);returnreplaceFrameGuts(cx,from,to,iter);}/* static */boolDebugger::handleIonBailout(JSContext*cx,jit::RematerializedFrame*from,jit::BaselineFrame*to){// When we return to a bailed-out Ion real frame, we must update all// Debugger.Frames that refer to its inline frames. However, since we// can't pop individual inline frames off the stack (we can only pop the// real frame that contains them all, as a unit), we cannot assume that// the frame we're dealing with is the top frame. Advance the iterator// across any inlined frames younger than |to|, the baseline frame// reconstructed during bailout from the Ion frame corresponding to// |from|.ScriptFrameIteriter(cx);while(iter.abstractFramePtr()!=to)++iter;returnreplaceFrameGuts(cx,from,to,iter);}/* static */voidDebugger::handleUnrecoverableIonBailoutError(JSContext*cx,jit::RematerializedFrame*frame){// Ion bailout can fail due to overrecursion. In such cases we cannot// honor any further Debugger hooks on the frame, and need to ensure that// its Debugger.Frame entry is cleaned up.removeFromFrameMapsAndClearBreakpointsIn(cx,frame);}/* static */voidDebugger::propagateForcedReturn(JSContext*cx,AbstractFramePtrframe,HandleValuerval){// Invoking the interrupt handler is considered a step and invokes the// youngest frame's onStep handler, if any. However, we cannot handle// { return: ... } resumption values straightforwardly from the interrupt// handler. Instead, we set the intended return value in the frame's rval// slot and set the propagating-forced-return flag on the JSContext.//// The interrupt handler then returns false with no exception set,// signaling an uncatchable exception. In the exception handlers, we then// check for the special propagating-forced-return flag.MOZ_ASSERT(!cx->isExceptionPending());cx->setPropagatingForcedReturn();frame.setReturnValue(rval);}structDebuggerScriptSetBreakpointMatcher{JSContext*cx_;Debugger*dbg_;size_toffset_;RootedObjecthandler_;public:explicitDebuggerScriptSetBreakpointMatcher(JSContext*cx,Debugger*dbg,size_toffset,HandleObjecthandler):cx_(cx),dbg_(dbg),offset_(offset),handler_(cx,handler){}usingReturnType=bool;ReturnTypematch(HandleScriptscript){if(!dbg_->observesScript(script)){JS_ReportErrorNumberASCII(cx_,GetErrorMessage,nullptr,JSMSG_DEBUG_NOT_DEBUGGING);returnfalse;}if(!EnsureScriptOffsetIsValid(cx_,script,offset_))returnfalse;// Ensure observability *before* setting the breakpoint. If the script is// not already a debuggee, trying to ensure observability after setting// the breakpoint (and thus marking the script as a debuggee) will skip// actually ensuring observability.if(!dbg_->ensureExecutionObservabilityOfScript(cx_,script))returnfalse;jsbytecode*pc=script->offsetToPC(offset_);BreakpointSite*site=script->getOrCreateBreakpointSite(cx_,pc);if(!site)returnfalse;site->inc(cx_->runtime()->defaultFreeOp());if(cx_->runtime()->new_<Breakpoint>(dbg_,site,handler_))returntrue;site->dec(cx_->runtime()->defaultFreeOp());site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());returnfalse;}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){wasm::Instance&instance=wasmInstance->instance();if(!instance.debug().hasBreakpointTrapAtOffset(offset_)){JS_ReportErrorNumberASCII(cx_,GetErrorMessage,nullptr,JSMSG_DEBUG_BAD_OFFSET);returnfalse;}WasmBreakpointSite*site=instance.debug().getOrCreateBreakpointSite(cx_,offset_);if(!site)returnfalse;site->inc(cx_->runtime()->defaultFreeOp());if(cx_->runtime()->new_<WasmBreakpoint>(dbg_,site,handler_,instance.object()))returntrue;site->dec(cx_->runtime()->defaultFreeOp());site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());returnfalse;}};staticboolDebuggerScript_setBreakpoint(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"setBreakpoint",args,obj,referent);if(!args.requireAtLeast(cx,"Debugger.Script.setBreakpoint",2))returnfalse;Debugger*dbg=Debugger::fromChildJSObject(obj);size_toffset;if(!ScriptOffset(cx,args[0],&offset))returnfalse;RootedObjecthandler(cx,NonNullObject(cx,args[1]));if(!handler)returnfalse;DebuggerScriptSetBreakpointMatchermatcher(cx,dbg,offset,handler);if(!referent.match(matcher))returnfalse;args.rval().setUndefined();returntrue;}staticboolDebuggerScript_getBreakpoints(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"getBreakpoints",args,obj,script);Debugger*dbg=Debugger::fromChildJSObject(obj);jsbytecode*pc;if(args.length()>0){size_toffset;if(!ScriptOffset(cx,args[0],&offset)||!EnsureScriptOffsetIsValid(cx,script,offset))returnfalse;pc=script->offsetToPC(offset);}else{pc=nullptr;}RootedObjectarr(cx,NewDenseEmptyArray(cx));if(!arr)returnfalse;for(unsignedi=0;i<script->length();i++){BreakpointSite*site=script->getBreakpointSite(script->offsetToPC(i));if(!site)continue;MOZ_ASSERT(site->type()==BreakpointSite::Type::JS);if(!pc||site->asJS()->pc==pc){for(Breakpoint*bp=site->firstBreakpoint();bp;bp=bp->nextInSite()){if(bp->debugger==dbg&&!NewbornArrayPush(cx,arr,ObjectValue(*bp->getHandler()))){returnfalse;}}}}args.rval().setObject(*arr);returntrue;}classDebuggerScriptClearBreakpointMatcher{JSContext*cx_;Debugger*dbg_;JSObject*handler_;public:explicitDebuggerScriptClearBreakpointMatcher(JSContext*cx,Debugger*dbg,JSObject*handler):cx_(cx),dbg_(dbg),handler_(handler){}usingReturnType=bool;ReturnTypematch(HandleScriptscript){script->clearBreakpointsIn(cx_->runtime()->defaultFreeOp(),dbg_,handler_);returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>instance){returninstance->instance().debug().clearBreakpointsIn(cx_,instance,dbg_,handler_);}};staticboolDebuggerScript_clearBreakpoint(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"clearBreakpoint",args,obj,referent);if(!args.requireAtLeast(cx,"Debugger.Script.clearBreakpoint",1))returnfalse;Debugger*dbg=Debugger::fromChildJSObject(obj);JSObject*handler=NonNullObject(cx,args[0]);if(!handler)returnfalse;DebuggerScriptClearBreakpointMatchermatcher(cx,dbg,handler);if(!referent.match(matcher))returnfalse;args.rval().setUndefined();returntrue;}staticboolDebuggerScript_clearAllBreakpoints(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"clearAllBreakpoints",args,obj,referent);Debugger*dbg=Debugger::fromChildJSObject(obj);DebuggerScriptClearBreakpointMatchermatcher(cx,dbg,nullptr);if(!referent.match(matcher))returnfalse;args.rval().setUndefined();returntrue;}classDebuggerScriptIsInCatchScopeMatcher{JSContext*cx_;size_toffset_;boolisInCatch_;public:explicitDebuggerScriptIsInCatchScopeMatcher(JSContext*cx,size_toffset):cx_(cx),offset_(offset){}usingReturnType=bool;inlineboolisInCatch()const{returnisInCatch_;}ReturnTypematch(HandleScriptscript){if(!EnsureScriptOffsetIsValid(cx_,script,offset_))returnfalse;/* * Try note ranges are relative to the mainOffset of the script, so adjust * offset accordingly. */size_toffset=offset_-script->mainOffset();if(script->hasTrynotes()){JSTryNote*tnBegin=script->trynotes()->vector;JSTryNote*tnEnd=tnBegin+script->trynotes()->length;while(tnBegin!=tnEnd){if(tnBegin->start<=offset&&offset<=tnBegin->start+tnBegin->length&&tnBegin->kind==JSTRY_CATCH){isInCatch_=true;returntrue;}++tnBegin;}}isInCatch_=false;returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>instance){isInCatch_=false;returntrue;}};staticboolDebuggerScript_isInCatchScope(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_REFERENT(cx,argc,vp,"isInCatchScope",args,obj,referent);if(!args.requireAtLeast(cx,"Debugger.Script.isInCatchScope",1))returnfalse;size_toffset;if(!ScriptOffset(cx,args[0],&offset))returnfalse;DebuggerScriptIsInCatchScopeMatchermatcher(cx,offset);if(!referent.match(matcher))returnfalse;args.rval().setBoolean(matcher.isInCatch());returntrue;}staticboolDebuggerScript_getOffsetsCoverage(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSCRIPT_SCRIPT(cx,argc,vp,"getOffsetsCoverage",args,obj,script);// If the script has no coverage information, then skip this and return null// instead.if(!script->hasScriptCounts()){args.rval().setNull();returntrue;}ScriptCounts*sc=&script->getScriptCounts();// If the main ever got visited, then assume that any code before main got// visited once.uint64_thits=0;constPCCounts*counts=sc->maybeGetPCCounts(script->pcToOffset(script->main()));if(counts->numExec())hits=1;// Build an array of objects which are composed of 4 properties:// - offset PC offset of the current opcode.// - lineNumber Line of the current opcode.// - columnNumber Column of the current opcode.// - count Number of times the instruction got executed.RootedObjectresult(cx,NewDenseEmptyArray(cx));if(!result)returnfalse;RootedIdoffsetId(cx,AtomToId(cx->names().offset));RootedIdlineNumberId(cx,AtomToId(cx->names().lineNumber));RootedIdcolumnNumberId(cx,AtomToId(cx->names().columnNumber));RootedIdcountId(cx,AtomToId(cx->names().count));RootedObjectitem(cx);RootedValueoffsetValue(cx);RootedValuelineNumberValue(cx);RootedValuecolumnNumberValue(cx);RootedValuecountValue(cx);// Iterate linearly over the bytecode.for(BytecodeRangeWithPositionr(cx,script);!r.empty();r.popFront()){size_toffset=r.frontOffset();// The beginning of each non-branching sequences of instruction set the// number of execution of the current instruction and any following// instruction.counts=sc->maybeGetPCCounts(offset);if(counts)hits=counts->numExec();offsetValue.setNumber(double(offset));lineNumberValue.setNumber(double(r.frontLineNumber()));columnNumberValue.setNumber(double(r.frontColumnNumber()));countValue.setNumber(double(hits));// Create a new object with the offset, line number, column number, the// number of hit counts, and append it to the array.item=NewObjectWithGivenProto<PlainObject>(cx,nullptr);if(!item||!DefineProperty(cx,item,offsetId,offsetValue)||!DefineProperty(cx,item,lineNumberId,lineNumberValue)||!DefineProperty(cx,item,columnNumberId,columnNumberValue)||!DefineProperty(cx,item,countId,countValue)||!NewbornArrayPush(cx,result,ObjectValue(*item))){returnfalse;}// If the current instruction has thrown, then decrement the hit counts// with the number of throws.counts=sc->maybeGetThrowCounts(offset);if(counts)hits-=counts->numExec();}args.rval().setObject(*result);returntrue;}staticboolDebuggerScript_construct(JSContext*cx,unsignedargc,Value*vp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NO_CONSTRUCTOR,"Debugger.Script");returnfalse;}staticconstJSPropertySpecDebuggerScript_properties[]={JS_PSG("displayName",DebuggerScript_getDisplayName,0),JS_PSG("url",DebuggerScript_getUrl,0),JS_PSG("startLine",DebuggerScript_getStartLine,0),JS_PSG("lineCount",DebuggerScript_getLineCount,0),JS_PSG("source",DebuggerScript_getSource,0),JS_PSG("sourceStart",DebuggerScript_getSourceStart,0),JS_PSG("sourceLength",DebuggerScript_getSourceLength,0),JS_PSG("global",DebuggerScript_getGlobal,0),JS_PSG("format",DebuggerScript_getFormat,0),JS_PS_END};staticconstJSFunctionSpecDebuggerScript_methods[]={JS_FN("getChildScripts",DebuggerScript_getChildScripts,0,0),JS_FN("getAllOffsets",DebuggerScript_getAllOffsets,0,0),JS_FN("getAllColumnOffsets",DebuggerScript_getAllColumnOffsets,0,0),JS_FN("getLineOffsets",DebuggerScript_getLineOffsets,1,0),JS_FN("getOffsetLocation",DebuggerScript_getOffsetLocation,0,0),JS_FN("setBreakpoint",DebuggerScript_setBreakpoint,2,0),JS_FN("getBreakpoints",DebuggerScript_getBreakpoints,1,0),JS_FN("clearBreakpoint",DebuggerScript_clearBreakpoint,1,0),JS_FN("clearAllBreakpoints",DebuggerScript_clearAllBreakpoints,0,0),JS_FN("isInCatchScope",DebuggerScript_isInCatchScope,1,0),JS_FN("getOffsetsCoverage",DebuggerScript_getOffsetsCoverage,0,0),JS_FS_END};/*** Debugger.Source *****************************************************************************/// For internal use only.staticinlineNativeObject*GetSourceReferentRawObject(JSObject*obj){MOZ_ASSERT(obj->getClass()==&DebuggerSource_class);returnstatic_cast<NativeObject*>(obj->as<NativeObject>().getPrivate());}staticinlineDebuggerSourceReferentGetSourceReferent(JSObject*obj){if(NativeObject*referent=GetSourceReferentRawObject(obj)){if(referent->is<ScriptSourceObject>())returnAsVariant(&referent->as<ScriptSourceObject>());returnAsVariant(&referent->as<WasmInstanceObject>());}returnAsVariant(static_cast<ScriptSourceObject*>(nullptr));}voidDebuggerSource_trace(JSTracer*trc,JSObject*obj){/* * There is a barrier on private pointers, so the Unbarriered marking * is okay. */if(JSObject*referent=GetSourceReferentRawObject(obj)){TraceManuallyBarrieredCrossCompartmentEdge(trc,obj,&referent,"Debugger.Source referent");obj->as<NativeObject>().setPrivateUnbarriered(referent);}}classSetDebuggerSourcePrivateMatcher{NativeObject*obj_;public:explicitSetDebuggerSourcePrivateMatcher(NativeObject*obj):obj_(obj){}usingReturnType=void;ReturnTypematch(HandleScriptSourcesource){obj_->setPrivateGCThing(source);}ReturnTypematch(Handle<WasmInstanceObject*>instance){obj_->setPrivateGCThing(instance);}};NativeObject*Debugger::newDebuggerSource(JSContext*cx,Handle<DebuggerSourceReferent>referent){assertSameCompartment(cx,object.get());RootedObjectproto(cx,&object->getReservedSlot(JSSLOT_DEBUG_SOURCE_PROTO).toObject());MOZ_ASSERT(proto);NativeObject*sourceobj=NewNativeObjectWithGivenProto(cx,&DebuggerSource_class,proto,TenuredObject);if(!sourceobj)returnnullptr;sourceobj->setReservedSlot(JSSLOT_DEBUGSOURCE_OWNER,ObjectValue(*object));SetDebuggerSourcePrivateMatchermatcher(sourceobj);referent.match(matcher);returnsourceobj;}JSObject*Debugger::wrapVariantReferent(JSContext*cx,Handle<DebuggerSourceReferent>referent){JSObject*obj;if(referent.is<ScriptSourceObject*>()){Handle<ScriptSourceObject*>untaggedReferent=referent.templateas<ScriptSourceObject*>();Rooted<CrossCompartmentKey>key(cx,CrossCompartmentKey(object,untaggedReferent,CrossCompartmentKey::DebuggerObjectKind::DebuggerSource));obj=wrapVariantReferent<DebuggerSourceReferent,ScriptSourceObject*,SourceWeakMap>(cx,sources,key,referent);}else{Handle<WasmInstanceObject*>untaggedReferent=referent.templateas<WasmInstanceObject*>();Rooted<CrossCompartmentKey>key(cx,CrossCompartmentKey(object,untaggedReferent,CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmSource));obj=wrapVariantReferent<DebuggerSourceReferent,WasmInstanceObject*,WasmInstanceWeakMap>(cx,wasmInstanceSources,key,referent);}MOZ_ASSERT_IF(obj,GetSourceReferent(obj)==referent);returnobj;}JSObject*Debugger::wrapSource(JSContext*cx,HandleScriptSourcesource){Rooted<DebuggerSourceReferent>referent(cx,source.get());returnwrapVariantReferent(cx,referent);}JSObject*Debugger::wrapWasmSource(JSContext*cx,Handle<WasmInstanceObject*>wasmInstance){Rooted<DebuggerSourceReferent>referent(cx,wasmInstance.get());returnwrapVariantReferent(cx,referent);}staticboolDebuggerSource_construct(JSContext*cx,unsignedargc,Value*vp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NO_CONSTRUCTOR,"Debugger.Source");returnfalse;}staticNativeObject*DebuggerSource_check(JSContext*cx,HandleValuethisv,constchar*fnname){JSObject*thisobj=NonNullObject(cx,thisv);if(!thisobj)returnnullptr;if(thisobj->getClass()!=&DebuggerSource_class){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Source",fnname,thisobj->getClass()->name);returnnullptr;}NativeObject*nthisobj=&thisobj->as<NativeObject>();if(!GetSourceReferentRawObject(thisobj)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Source",fnname,"prototype object");returnnullptr;}returnnthisobj;}template<typenameReferentT>staticNativeObject*DebuggerSource_checkThis(JSContext*cx,constCallArgs&args,constchar*fnname,constchar*refname){NativeObject*thisobj=DebuggerSource_check(cx,args.thisv(),fnname);if(!thisobj)returnnullptr;if(!GetSourceReferent(thisobj).is<ReferentT>()){ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_BAD_REFERENT,JSDVG_SEARCH_STACK,args.thisv(),nullptr,refname,nullptr);returnnullptr;}returnthisobj;}#define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, referent) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedNativeObject obj(cx, DebuggerSource_check(cx, args.thisv(), fnname)); \ if (!obj) \ return false; \ Rooted<DebuggerSourceReferent> referent(cx, GetSourceReferent(obj))#define THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, fnname, args, obj, sourceObject) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedNativeObject obj(cx, \ DebuggerSource_checkThis<ScriptSourceObject*>(cx, args, fnname, \ "a JS source")); \ if (!obj) \ return false; \ RootedScriptSource sourceObject(cx, GetSourceReferent(obj).as<ScriptSourceObject*>())classDebuggerSourceGetTextMatcher{JSContext*cx_;public:explicitDebuggerSourceGetTextMatcher(JSContext*cx):cx_(cx){}usingReturnType=JSString*;ReturnTypematch(HandleScriptSourcesourceObject){ScriptSource*ss=sourceObject->source();boolhasSourceData=ss->hasSourceData();if(!ss->hasSourceData()&&!JSScript::loadSource(cx_,ss,&hasSourceData))returnnullptr;if(!hasSourceData)returnNewStringCopyZ<CanGC>(cx_,"[no source]");if(ss->isFunctionBody())returnss->functionBodyString(cx_);returnss->substring(cx_,0,ss->length());}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){if(wasmInstance->instance().debug().maybeBytecode()&&wasmInstance->instance().debug().binarySource()){returnNewStringCopyZ<CanGC>(cx_,"[wasm]");}returnwasmInstance->instance().debug().createText(cx_);}};staticboolDebuggerSource_getText(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get text)",args,obj,referent);Valuetextv=obj->getReservedSlot(JSSLOT_DEBUGSOURCE_TEXT);if(!textv.isUndefined()){MOZ_ASSERT(textv.isString());args.rval().set(textv);returntrue;}DebuggerSourceGetTextMatchermatcher(cx);JSString*str=referent.match(matcher);if(!str)returnfalse;args.rval().setString(str);obj->setReservedSlot(JSSLOT_DEBUGSOURCE_TEXT,args.rval());returntrue;}staticboolDebuggerSource_getBinary(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get binary)",args,obj,referent);if(!referent.is<WasmInstanceObject*>()){ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_BAD_REFERENT,JSDVG_SEARCH_STACK,args.thisv(),nullptr,"a wasm source",nullptr);returnfalse;}RootedWasmInstanceObjectwasmInstance(cx,referent.as<WasmInstanceObject*>());if(!wasmInstance->instance().debug().binarySource()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_NO_BINARY_SOURCE);returnfalse;}autobytecode=wasmInstance->instance().debug().maybeBytecode();size_tarrLength=bytecode?bytecode->length():0;RootedObjectarr(cx,JS_NewUint8Array(cx,arrLength));if(!arr)returnfalse;if(bytecode)memcpy(arr->as<TypedArrayObject>().viewDataUnshared(),bytecode->begin(),arrLength);args.rval().setObject(*arr);returntrue;}classDebuggerSourceGetURLMatcher{JSContext*cx_;public:explicitDebuggerSourceGetURLMatcher(JSContext*cx):cx_(cx){}usingReturnType=Maybe<JSString*>;ReturnTypematch(HandleScriptSourcesourceObject){ScriptSource*ss=sourceObject->source();MOZ_ASSERT(ss);if(ss->filename()){JSString*str=NewStringCopyZ<CanGC>(cx_,ss->filename());returnSome(str);}returnNothing();}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){if(JSString*str=wasmInstance->instance().debug().debugDisplayURL(cx_))returnSome(str);returnNothing();}};staticboolDebuggerSource_getURL(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get url)",args,obj,referent);DebuggerSourceGetURLMatchermatcher(cx);Maybe<JSString*>str=referent.match(matcher);if(str.isSome()){if(!*str)returnfalse;args.rval().setString(*str);}else{args.rval().setNull();}returntrue;}structDebuggerSourceGetDisplayURLMatcher{usingReturnType=constchar16_t*;ReturnTypematch(HandleScriptSourcesourceObject){ScriptSource*ss=sourceObject->source();MOZ_ASSERT(ss);returnss->hasDisplayURL()?ss->displayURL():nullptr;}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){returnwasmInstance->instance().metadata().displayURL();}};staticboolDebuggerSource_getDisplayURL(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get url)",args,obj,referent);DebuggerSourceGetDisplayURLMatchermatcher;if(constchar16_t*displayURL=referent.match(matcher)){JSString*str=JS_NewUCStringCopyZ(cx,displayURL);if(!str)returnfalse;args.rval().setString(str);}else{args.rval().setNull();}returntrue;}structDebuggerSourceGetElementMatcher{usingReturnType=JSObject*;ReturnTypematch(HandleScriptSourcesourceObject){returnsourceObject->element();}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){returnnullptr;}};staticboolDebuggerSource_getElement(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get element)",args,obj,referent);DebuggerSourceGetElementMatchermatcher;if(JSObject*element=referent.match(matcher)){args.rval().setObjectOrNull(element);if(!Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx,args.rval()))returnfalse;}else{args.rval().setUndefined();}returntrue;}structDebuggerSourceGetElementPropertyMatcher{usingReturnType=Value;ReturnTypematch(HandleScriptSourcesourceObject){returnsourceObject->elementAttributeName();}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){returnUndefinedValue();}};staticboolDebuggerSource_getElementProperty(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get elementAttributeName)",args,obj,referent);DebuggerSourceGetElementPropertyMatchermatcher;args.rval().set(referent.match(matcher));returnDebugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx,args.rval());}classDebuggerSourceGetIntroductionScriptMatcher{JSContext*cx_;Debugger*dbg_;MutableHandleValuerval_;public:DebuggerSourceGetIntroductionScriptMatcher(JSContext*cx,Debugger*dbg,MutableHandleValuerval):cx_(cx),dbg_(dbg),rval_(rval){}usingReturnType=bool;ReturnTypematch(HandleScriptSourcesourceObject){RootedScriptscript(cx_,sourceObject->introductionScript());if(script){RootedObjectscriptDO(cx_,dbg_->wrapScript(cx_,script));if(!scriptDO)returnfalse;rval_.setObject(*scriptDO);}else{rval_.setUndefined();}returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){RootedObjectds(cx_,dbg_->wrapWasmScript(cx_,wasmInstance));if(!ds)returnfalse;rval_.setObject(*ds);returntrue;}};staticboolDebuggerSource_getIntroductionScript(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get introductionScript)",args,obj,referent);Debugger*dbg=Debugger::fromChildJSObject(obj);DebuggerSourceGetIntroductionScriptMatchermatcher(cx,dbg,args.rval());returnreferent.match(matcher);}structDebuggerGetIntroductionOffsetMatcher{usingReturnType=Value;ReturnTypematch(HandleScriptSourcesourceObject){// Regardless of what's recorded in the ScriptSourceObject and// ScriptSource, only hand out the introduction offset if we also have// the script within which it applies.ScriptSource*ss=sourceObject->source();if(ss->hasIntroductionOffset()&&sourceObject->introductionScript())returnInt32Value(ss->introductionOffset());returnUndefinedValue();}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){returnUndefinedValue();}};staticboolDebuggerSource_getIntroductionOffset(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get introductionOffset)",args,obj,referent);DebuggerGetIntroductionOffsetMatchermatcher;args.rval().set(referent.match(matcher));returntrue;}structDebuggerSourceGetIntroductionTypeMatcher{usingReturnType=constchar*;ReturnTypematch(HandleScriptSourcesourceObject){ScriptSource*ss=sourceObject->source();MOZ_ASSERT(ss);returnss->hasIntroductionType()?ss->introductionType():nullptr;}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){return"wasm";}};staticboolDebuggerSource_getIntroductionType(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get introductionType)",args,obj,referent);DebuggerSourceGetIntroductionTypeMatchermatcher;if(constchar*introductionType=referent.match(matcher)){JSString*str=NewStringCopyZ<CanGC>(cx,introductionType);if(!str)returnfalse;args.rval().setString(str);}else{args.rval().setUndefined();}returntrue;}staticboolDebuggerSource_setSourceMapURL(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_SOURCE(cx,argc,vp,"sourceMapURL",args,obj,sourceObject);ScriptSource*ss=sourceObject->source();MOZ_ASSERT(ss);JSString*str=ToString<CanGC>(cx,args[0]);if(!str)returnfalse;AutoStableStringCharsstableChars(cx);if(!stableChars.initTwoByte(cx,str))returnfalse;if(!ss->setSourceMapURL(cx,stableChars.twoByteChars()))returnfalse;args.rval().setUndefined();returntrue;}classDebuggerSourceGetSourceMapURLMatcher{JSContext*cx_;MutableHandleStringresult_;public:explicitDebuggerSourceGetSourceMapURLMatcher(JSContext*cx,MutableHandleStringresult):cx_(cx),result_(result){}usingReturnType=bool;ReturnTypematch(HandleScriptSourcesourceObject){ScriptSource*ss=sourceObject->source();MOZ_ASSERT(ss);if(!ss->hasSourceMapURL()){result_.set(nullptr);returntrue;}JSString*str=JS_NewUCStringCopyZ(cx_,ss->sourceMapURL());if(!str)returnfalse;result_.set(str);returntrue;}ReturnTypematch(Handle<WasmInstanceObject*>wasmInstance){// sourceMapURL is not available if debugger was not in// allowWasmBinarySource mode.if(!wasmInstance->instance().debug().binarySource()){result_.set(nullptr);returntrue;}RootedStringstr(cx_);if(!wasmInstance->instance().debug().getSourceMappingURL(cx_,&str))returnfalse;result_.set(str);returntrue;}};staticboolDebuggerSource_getSourceMapURL(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGSOURCE_REFERENT(cx,argc,vp,"(get sourceMapURL)",args,obj,referent);RootedStringresult(cx);DebuggerSourceGetSourceMapURLMatchermatcher(cx,&result);if(!referent.match(matcher))returnfalse;if(result)args.rval().setString(result);elseargs.rval().setNull();returntrue;}staticconstJSPropertySpecDebuggerSource_properties[]={JS_PSG("text",DebuggerSource_getText,0),JS_PSG("binary",DebuggerSource_getBinary,0),JS_PSG("url",DebuggerSource_getURL,0),JS_PSG("element",DebuggerSource_getElement,0),JS_PSG("displayURL",DebuggerSource_getDisplayURL,0),JS_PSG("introductionScript",DebuggerSource_getIntroductionScript,0),JS_PSG("introductionOffset",DebuggerSource_getIntroductionOffset,0),JS_PSG("introductionType",DebuggerSource_getIntroductionType,0),JS_PSG("elementAttributeName",DebuggerSource_getElementProperty,0),JS_PSGS("sourceMapURL",DebuggerSource_getSourceMapURL,DebuggerSource_setSourceMapURL,0),JS_PS_END};staticconstJSFunctionSpecDebuggerSource_methods[]={JS_FS_END};/*** Debugger.Frame ******************************************************************************/ScriptedOnStepHandler::ScriptedOnStepHandler(JSObject*object):object_(object){MOZ_ASSERT(object_->isCallable());}JSObject*ScriptedOnStepHandler::object()const{returnobject_;}voidScriptedOnStepHandler::drop(){this->~ScriptedOnStepHandler();js_free(this);}voidScriptedOnStepHandler::trace(JSTracer*tracer){TraceEdge(tracer,&object_,"OnStepHandlerFunction.object");}boolScriptedOnStepHandler::onStep(JSContext*cx,HandleDebuggerFrameframe,JSTrapStatus&statusp,MutableHandleValuevp){RootedValuefval(cx,ObjectValue(*object_));RootedValuerval(cx);if(!js::Call(cx,fval,frame,&rval))returnfalse;returnParseResumptionValue(cx,rval,statusp,vp);};ScriptedOnPopHandler::ScriptedOnPopHandler(JSObject*object):object_(object){MOZ_ASSERT(object->isCallable());}JSObject*ScriptedOnPopHandler::object()const{returnobject_;}voidScriptedOnPopHandler::drop(){this->~ScriptedOnPopHandler();js_free(this);}voidScriptedOnPopHandler::trace(JSTracer*tracer){TraceEdge(tracer,&object_,"OnStepHandlerFunction.object");}boolScriptedOnPopHandler::onPop(JSContext*cx,HandleDebuggerFrameframe,JSTrapStatus&statusp,MutableHandleValuevp){Debugger*dbg=frame->owner();RootedValuecompletion(cx);if(!dbg->newCompletionValue(cx,statusp,vp,&completion))returnfalse;RootedValuefval(cx,ObjectValue(*object_));RootedValuerval(cx);if(!js::Call(cx,fval,frame,completion,&rval))returnfalse;returnParseResumptionValue(cx,rval,statusp,vp);};/* static */NativeObject*DebuggerFrame::initClass(JSContext*cx,HandleObjectdbgCtor,HandleObjectobj){Handle<GlobalObject*>global=obj.as<GlobalObject>();RootedObjectobjProto(cx,GlobalObject::getOrCreateObjectPrototype(cx,global));returnInitClass(cx,dbgCtor,objProto,&class_,construct,0,properties_,methods_,nullptr,nullptr);}/* static */DebuggerFrame*DebuggerFrame::create(JSContext*cx,HandleObjectproto,AbstractFramePtrreferent,constFrameIter*maybeIter,HandleNativeObjectdebugger){JSObject*obj=NewObjectWithGivenProto(cx,&DebuggerFrame::class_,proto);if(!obj)returnnullptr;DebuggerFrame&frame=obj->as<DebuggerFrame>();// Eagerly copy FrameIter data if we've already walked the stack.if(maybeIter){AbstractFramePtrdata=maybeIter->copyDataAsAbstractFramePtr();if(!data)returnnullptr;frame.setPrivate(data.raw());}else{frame.setPrivate(referent.raw());}frame.setReservedSlot(JSSLOT_DEBUGFRAME_OWNER,ObjectValue(*debugger));return&frame;}/* static */boolDebuggerFrame::getCallee(JSContext*cx,HandleDebuggerFrameframe,MutableHandleDebuggerObjectresult){MOZ_ASSERT(frame->isLive());AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);if(!referent.isFunctionFrame()){result.set(nullptr);returntrue;}Debugger*dbg=frame->owner();RootedObjectcallee(cx,referent.callee());returndbg->wrapDebuggeeObject(cx,callee,result);}/* static */boolDebuggerFrame::getIsConstructing(JSContext*cx,HandleDebuggerFrameframe,bool&result){MOZ_ASSERT(frame->isLive());Maybe<FrameIter>maybeIter;if(!DebuggerFrame::getFrameIter(cx,frame,maybeIter))returnfalse;FrameIter&iter=*maybeIter;result=iter.isFunctionFrame()&&iter.isConstructing();returntrue;}staticvoidUpdateFrameIterPc(FrameIter&iter){if(iter.abstractFramePtr().isWasmDebugFrame()){// Wasm debug frames don't need their pc updated -- it's null.return;}if(iter.abstractFramePtr().isRematerializedFrame()){#ifdef DEBUG// Rematerialized frames don't need their pc updated. The reason we// need to update pc is because we might get the same Debugger.Frame// object for multiple re-entries into debugger code from debuggee// code. This reentrancy is not possible with rematerialized frames,// because when returning to debuggee code, we would have bailed out// to baseline.//// We walk the stack to assert that it doesn't need updating.jit::RematerializedFrame*frame=iter.abstractFramePtr().asRematerializedFrame();jit::JitFrameLayout*jsFrame=(jit::JitFrameLayout*)frame->top();jit::JitActivation*activation=iter.activation()->asJit();JSContext*cx=TlsContext.get();MOZ_ASSERT(cx==activation->cx());ActivationIteratoractivationIter(cx);while(activationIter.activation()!=activation)++activationIter;jit::JitFrameIteratorjitIter(activationIter);while(!jitIter.isIonJS()||jitIter.jsFrame()!=jsFrame)++jitIter;jit::InlineFrameIteratorionInlineIter(cx,&jitIter);while(ionInlineIter.frameNo()!=frame->frameNo())++ionInlineIter;MOZ_ASSERT(ionInlineIter.pc()==iter.pc());#endifreturn;}iter.updatePcQuadratic();}/* static */boolDebuggerFrame::getEnvironment(JSContext*cx,HandleDebuggerFrameframe,MutableHandleDebuggerEnvironmentresult){MOZ_ASSERT(frame->isLive());Debugger*dbg=frame->owner();Maybe<FrameIter>maybeIter;if(!DebuggerFrame::getFrameIter(cx,frame,maybeIter))returnfalse;FrameIter&iter=*maybeIter;Rooted<Env*>env(cx);{AutoCompartmentac(cx,iter.abstractFramePtr().environmentChain());UpdateFrameIterPc(iter);env=GetDebugEnvironmentForFrame(cx,iter.abstractFramePtr(),iter.pc());if(!env)returnfalse;}returndbg->wrapEnvironment(cx,env,result);}/* static */boolDebuggerFrame::getIsGenerator(HandleDebuggerFrameframe){AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);returnreferent.hasScript()&&(referent.script()->isStarGenerator()||referent.script()->isLegacyGenerator());}/* static */boolDebuggerFrame::getOffset(JSContext*cx,HandleDebuggerFrameframe,size_t&result){MOZ_ASSERT(frame->isLive());Maybe<FrameIter>maybeIter;if(!DebuggerFrame::getFrameIter(cx,frame,maybeIter))returnfalse;FrameIter&iter=*maybeIter;AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);if(referent.isWasmDebugFrame()){iter.wasmUpdateBytecodeOffset();result=iter.wasmBytecodeOffset();}else{JSScript*script=iter.script();UpdateFrameIterPc(iter);jsbytecode*pc=iter.pc();result=script->pcToOffset(pc);}returntrue;}/* static */boolDebuggerFrame::getOlder(JSContext*cx,HandleDebuggerFrameframe,MutableHandleDebuggerFrameresult){MOZ_ASSERT(frame->isLive());Debugger*dbg=frame->owner();Maybe<FrameIter>maybeIter;if(!DebuggerFrame::getFrameIter(cx,frame,maybeIter))returnfalse;FrameIter&iter=*maybeIter;for(++iter;!iter.done();++iter){if(dbg->observesFrame(iter)){if(iter.isIon()&&!iter.ensureHasRematerializedFrame(cx))returnfalse;returndbg->getScriptFrame(cx,iter,result);}}result.set(nullptr);returntrue;}/* static */boolDebuggerFrame::getThis(JSContext*cx,HandleDebuggerFrameframe,MutableHandleValueresult){MOZ_ASSERT(frame->isLive());if(!requireScriptReferent(cx,frame))returnfalse;Debugger*dbg=frame->owner();Maybe<FrameIter>maybeIter;if(!DebuggerFrame::getFrameIter(cx,frame,maybeIter))returnfalse;FrameIter&iter=*maybeIter;{AbstractFramePtrframe=iter.abstractFramePtr();AutoCompartmentac(cx,frame.environmentChain());UpdateFrameIterPc(iter);if(!GetThisValueForDebuggerMaybeOptimizedOut(cx,frame,iter.pc(),result))returnfalse;}returndbg->wrapDebuggeeValue(cx,result);}/* static */DebuggerFrameTypeDebuggerFrame::getType(HandleDebuggerFrameframe){AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);/* * Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the * order of checks here is significant. */if(referent.isEvalFrame())returnDebuggerFrameType::Eval;elseif(referent.isGlobalFrame())returnDebuggerFrameType::Global;elseif(referent.isFunctionFrame())returnDebuggerFrameType::Call;elseif(referent.isModuleFrame())returnDebuggerFrameType::Module;elseif(referent.isWasmDebugFrame())returnDebuggerFrameType::WasmCall;MOZ_CRASH("Unknown frame type");}/* static */DebuggerFrameImplementationDebuggerFrame::getImplementation(HandleDebuggerFrameframe){AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);if(referent.isBaselineFrame())returnDebuggerFrameImplementation::Baseline;elseif(referent.isRematerializedFrame())returnDebuggerFrameImplementation::Ion;elseif(referent.isWasmDebugFrame())returnDebuggerFrameImplementation::Wasm;returnDebuggerFrameImplementation::Interpreter;}/* * If succesful, transfers the ownership of the given `handler` to this * Debugger.Frame. Note that on failure, the ownership of `handler` is not * transferred, and the caller is responsible for cleaning it up. *//* static */boolDebuggerFrame::setOnStepHandler(JSContext*cx,HandleDebuggerFrameframe,OnStepHandler*handler){MOZ_ASSERT(frame->isLive());OnStepHandler*prior=frame->onStepHandler();if(prior&&handler!=prior){prior->drop();}AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);if(referent.isWasmDebugFrame()){wasm::Instance*instance=referent.asWasmDebugFrame()->instance();wasm::DebugFrame*wasmFrame=referent.asWasmDebugFrame();if(handler&&!prior){// Single stepping toggled off->on.if(!instance->debug().incrementStepModeCount(cx,wasmFrame->funcIndex()))returnfalse;}elseif(!handler&&prior){// Single stepping toggled on->off.FreeOp*fop=cx->runtime()->defaultFreeOp();if(!instance->debug().decrementStepModeCount(fop,wasmFrame->funcIndex()))returnfalse;}}else{if(handler&&!prior){// Single stepping toggled off->on.AutoCompartmentac(cx,referent.environmentChain());// Ensure observability *before* incrementing the step mode count.// Calling this function after calling incrementStepModeCount// will make it a no-op.Debugger*dbg=frame->owner();if(!dbg->ensureExecutionObservabilityOfScript(cx,referent.script()))returnfalse;if(!referent.script()->incrementStepModeCount(cx))returnfalse;}elseif(!handler&&prior){// Single stepping toggled on->off.referent.script()->decrementStepModeCount(cx->runtime()->defaultFreeOp());}}/* Now that the step mode switch has succeeded, we can install the handler. */frame->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,handler?PrivateValue(handler):UndefinedValue());returntrue;}/* static */boolDebuggerFrame::getArguments(JSContext*cx,HandleDebuggerFrameframe,MutableHandleDebuggerArgumentsresult){Valueargumentsv=frame->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS);if(!argumentsv.isUndefined()){result.set(argumentsv.isObject()?&argumentsv.toObject().as<DebuggerArguments>():nullptr);returntrue;}AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);RootedDebuggerArgumentsarguments(cx);if(referent.hasArgs()){Rooted<GlobalObject*>global(cx,&frame->global());RootedObjectproto(cx,GlobalObject::getOrCreateArrayPrototype(cx,global));if(!proto)returnfalse;arguments=DebuggerArguments::create(cx,proto,frame);}else{arguments=nullptr;}result.set(arguments);frame->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS,ObjectOrNullValue(result));returntrue;}/* * Evaluate |chars[0..length-1]| in the environment |env|, treating that * source as appearing starting at |lineno| in |filename|. Store the return * value in |*rval|. Use |thisv| as the 'this' value. * * If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env| * must be either |frame|'s DebugScopeObject, or some extension of that * environment; either way, |frame|'s scope is where newly declared variables * go. In this case, |frame| must have a computed 'this' value, equal to |thisv|. */staticboolEvaluateInEnv(JSContext*cx,Handle<Env*>env,AbstractFramePtrframe,jsbytecode*pc,mozilla::Range<constchar16_t>chars,constchar*filename,unsignedlineno,MutableHandleValuerval){assertSameCompartment(cx,env,frame);MOZ_ASSERT_IF(frame,pc);CompileOptionsoptions(cx);options.setIsRunOnce(true).setNoScriptRval(false).setFileAndLine(filename,lineno).setCanLazilyParse(false).setIntroductionType("debugger eval").maybeMakeStrictMode(frame?frame.script()->strict():false);RootedScriptcallerScript(cx,frame?frame.script():nullptr);SourceBufferHoldersrcBuf(chars.begin().get(),chars.length(),SourceBufferHolder::NoOwnership);RootedScriptscript(cx);ScopeKindscopeKind;if(IsGlobalLexicalEnvironment(env))scopeKind=ScopeKind::Global;elsescopeKind=ScopeKind::NonSyntactic;if(frame){MOZ_ASSERT(scopeKind==ScopeKind::NonSyntactic);RootedScopescope(cx,GlobalScope::createEmpty(cx,ScopeKind::NonSyntactic));if(!scope)returnfalse;script=frontend::CompileEvalScript(cx,cx->tempLifoAlloc(),env,scope,options,srcBuf);if(script)script->setActiveEval();}else{// Do not consider executeInGlobal{WithBindings} as an eval, but instead// as executing a series of statements at the global level. This is to// circumvent the fresh lexical scope that all eval have, so that the// users of executeInGlobal, like the web console, may add new bindings to// the global scope.script=frontend::CompileGlobalScript(cx,cx->tempLifoAlloc(),scopeKind,options,srcBuf);}if(!script)returnfalse;returnExecuteKernel(cx,script,*env,NullValue(),frame,rval.address());}staticboolDebuggerGenericEval(JSContext*cx,constmozilla::Range<constchar16_t>chars,HandleObjectbindings,constEvalOptions&options,JSTrapStatus&status,MutableHandleValuevalue,Debugger*dbg,HandleObjectenvArg,FrameIter*iter){/* Either we're specifying the frame, or a global. */MOZ_ASSERT_IF(iter,!envArg);MOZ_ASSERT_IF(!iter,envArg&&IsGlobalLexicalEnvironment(envArg));/* * Gather keys and values of bindings, if any. This must be done in the * debugger compartment, since that is where any exceptions must be * thrown. */AutoIdVectorkeys(cx);AutoValueVectorvalues(cx);if(bindings){if(!GetPropertyKeys(cx,bindings,JSITER_OWNONLY,&keys)||!values.growBy(keys.length())){returnfalse;}for(size_ti=0;i<keys.length();i++){MutableHandleValuevalp=values[i];if(!GetProperty(cx,bindings,bindings,keys[i],valp)||!dbg->unwrapDebuggeeValue(cx,valp)){returnfalse;}}}Maybe<AutoCompartment>ac;if(iter)ac.emplace(cx,iter->environmentChain(cx));elseac.emplace(cx,envArg);Rooted<Env*>env(cx);if(iter){env=GetDebugEnvironmentForFrame(cx,iter->abstractFramePtr(),iter->pc());if(!env)returnfalse;}else{env=envArg;}/* If evalWithBindings, create the inner environment. */if(bindings){RootedPlainObjectnenv(cx,NewObjectWithGivenProto<PlainObject>(cx,nullptr));if(!nenv)returnfalse;RootedIdid(cx);for(size_ti=0;i<keys.length();i++){id=keys[i];cx->markId(id);MutableHandleValueval=values[i];if(!cx->compartment()->wrap(cx,val)||!NativeDefineProperty(cx,nenv,id,val,nullptr,nullptr,0)){returnfalse;}}AutoObjectVectorenvChain(cx);if(!envChain.append(nenv))returnfalse;RootedObjectnewEnv(cx);if(!CreateObjectsForEnvironmentChain(cx,envChain,env,&newEnv))returnfalse;env=newEnv;}/* Run the code and produce the completion value. */LeaveDebuggeeNoExecutennx(cx);RootedValuerval(cx);AbstractFramePtrframe=iter?iter->abstractFramePtr():NullFramePtr();jsbytecode*pc=iter?iter->pc():nullptr;boolok=EvaluateInEnv(cx,env,frame,pc,chars,options.filename()?options.filename():"debugger eval code",options.lineno(),&rval);Debugger::resultToCompletion(cx,ok,rval,&status,value);ac.reset();returndbg->wrapDebuggeeValue(cx,value);}/* static */boolDebuggerFrame::eval(JSContext*cx,HandleDebuggerFrameframe,mozilla::Range<constchar16_t>chars,HandleObjectbindings,constEvalOptions&options,JSTrapStatus&status,MutableHandleValuevalue){MOZ_ASSERT(frame->isLive());if(!requireScriptReferent(cx,frame))returnfalse;Debugger*dbg=frame->owner();Maybe<FrameIter>maybeIter;if(!DebuggerFrame::getFrameIter(cx,frame,maybeIter))returnfalse;FrameIter&iter=*maybeIter;UpdateFrameIterPc(iter);returnDebuggerGenericEval(cx,chars,bindings,options,status,value,dbg,nullptr,&iter);}/* statuc */boolDebuggerFrame::isLive()const{return!!getPrivate();}OnStepHandler*DebuggerFrame::onStepHandler()const{Valuevalue=getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);returnvalue.isUndefined()?nullptr:static_cast<OnStepHandler*>(value.toPrivate());}OnPopHandler*DebuggerFrame::onPopHandler()const{Valuevalue=getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER);returnvalue.isUndefined()?nullptr:static_cast<OnPopHandler*>(value.toPrivate());}voidDebuggerFrame::setOnPopHandler(OnPopHandler*handler){MOZ_ASSERT(isLive());OnPopHandler*prior=onPopHandler();if(prior&&prior!=handler)prior->drop();setReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER,handler?PrivateValue(handler):UndefinedValue());}staticboolDebuggerFrame_requireLive(JSContext*cx,HandleDebuggerFrameframe){if(!frame->isLive()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_NOT_LIVE,"Debugger.Frame");returnfalse;}returntrue;}/* static */AbstractFramePtrDebuggerFrame::getReferent(HandleDebuggerFrameframe){AbstractFramePtrreferent=AbstractFramePtr::FromRaw(frame->getPrivate());if(referent.isScriptFrameIterData()){FrameIteriter(*(FrameIter::Data*)(referent.raw()));referent=iter.abstractFramePtr();}returnreferent;}/* static */boolDebuggerFrame::getFrameIter(JSContext*cx,HandleDebuggerFrameframe,Maybe<FrameIter>&result){AbstractFramePtrreferent=AbstractFramePtr::FromRaw(frame->getPrivate());if(referent.isScriptFrameIterData()){result.emplace(*reinterpret_cast<FrameIter::Data*>(referent.raw()));}else{result.emplace(cx,FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK);FrameIter&iter=*result;while(!iter.hasUsableAbstractFramePtr()||iter.abstractFramePtr()!=referent)++iter;AbstractFramePtrdata=iter.copyDataAsAbstractFramePtr();if(!data)returnfalse;frame->setPrivate(data.raw());}returntrue;}/* static */boolDebuggerFrame::requireScriptReferent(JSContext*cx,HandleDebuggerFrameframe){AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);if(!referent.hasScript()){RootedValueframeobj(cx,ObjectValue(*frame));ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_BAD_REFERENT,JSDVG_SEARCH_STACK,frameobj,nullptr,"a script frame",nullptr);returnfalse;}returntrue;}staticvoidDebuggerFrame_freeScriptFrameIterData(FreeOp*fop,JSObject*obj){AbstractFramePtrframe=AbstractFramePtr::FromRaw(obj->as<NativeObject>().getPrivate());if(frame.isScriptFrameIterData())fop->delete_((FrameIter::Data*)frame.raw());obj->as<NativeObject>().setPrivate(nullptr);}staticvoidDebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp*fop,AbstractFramePtrframe,NativeObject*frameobj){/* If this frame has an onStep handler, decrement the script's count. */if(frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())return;if(frame.isWasmDebugFrame()){wasm::Instance*instance=frame.wasmInstance();instance->debug().decrementStepModeCount(fop,frame.asWasmDebugFrame()->funcIndex());}else{frame.script()->decrementStepModeCount(fop);}}staticvoidDebuggerFrame_finalize(FreeOp*fop,JSObject*obj){MOZ_ASSERT(fop->maybeOnHelperThread());DebuggerFrame_freeScriptFrameIterData(fop,obj);OnStepHandler*onStepHandler=obj->as<DebuggerFrame>().onStepHandler();if(onStepHandler)onStepHandler->drop();OnPopHandler*onPopHandler=obj->as<DebuggerFrame>().onPopHandler();if(onPopHandler)onPopHandler->drop();}staticvoidDebuggerFrame_trace(JSTracer*trc,JSObject*obj){OnStepHandler*onStepHandler=obj->as<DebuggerFrame>().onStepHandler();if(onStepHandler)onStepHandler->trace(trc);OnPopHandler*onPopHandler=obj->as<DebuggerFrame>().onPopHandler();if(onPopHandler)onPopHandler->trace(trc);}staticDebuggerFrame*DebuggerFrame_checkThis(JSContext*cx,constCallArgs&args,constchar*fnname,boolcheckLive){JSObject*thisobj=NonNullObject(cx,args.thisv());if(!thisobj)returnnullptr;if(thisobj->getClass()!=&DebuggerFrame::class_){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Frame",fnname,thisobj->getClass()->name);returnnullptr;}RootedDebuggerFrameframe(cx,&thisobj->as<DebuggerFrame>());/* * Forbid Debugger.Frame.prototype, which is of class DebuggerFrame::class_ * but isn't really a working Debugger.Frame object. The prototype object * is distinguished by having a nullptr private value. Also, forbid popped * frames. */if(!frame->getPrivate()&&frame->getReservedSlot(JSSLOT_DEBUGFRAME_OWNER).isUndefined()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Frame",fnname,"prototype object");returnnullptr;}if(checkLive){if(!DebuggerFrame_requireLive(cx,frame))returnnullptr;}returnframe;}/* * To make frequently fired hooks like onEnterFrame more performant, * Debugger.Frame methods should not create a FrameIter unless it * absolutely needs to. That is, unless the method has to call a method on * FrameIter that's otherwise not available on AbstractFramePtr. * * When a Debugger.Frame is first created, its private slot is set to the * AbstractFramePtr itself. The first time the users asks for a * FrameIter, we construct one, have it settle on the frame pointed to * by the AbstractFramePtr and cache its internal Data in the Debugger.Frame * object's private slot. Subsequent uses of the Debugger.Frame object will * always create a FrameIter from the cached Data. * * Methods that only need the AbstractFramePtr should use THIS_FRAME. * Methods that need a FrameIterator should use THIS_FRAME_ITER. */#define THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, frame) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedDebuggerFrame frame(cx, DebuggerFrame_checkThis(cx, args, fnname, true)); \ if (!frame) \ return false;#define THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedNativeObject thisobj(cx, DebuggerFrame_checkThis(cx, args, fnname, true)); \ if (!thisobj) \ return false#define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame) \ THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \ AbstractFramePtr frame = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \ if (frame.isScriptFrameIterData()) { \ FrameIter iter(*(FrameIter::Data*)(frame.raw())); \ frame = iter.abstractFramePtr(); \ }#define THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter) \ THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \ Maybe<FrameIter> maybeIter; \ { \ AbstractFramePtr f = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \ if (f.isScriptFrameIterData()) { \ maybeIter.emplace(*(FrameIter::Data*)(f.raw())); \ } else { \ maybeIter.emplace(cx, FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK); \ FrameIter& iter = *maybeIter; \ while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != f) \ ++iter; \ AbstractFramePtr data = iter.copyDataAsAbstractFramePtr(); \ if (!data) \ return false; \ thisobj->setPrivate(data.raw()); \ } \ } \ FrameIter& iter = *maybeIter#define THIS_FRAME_OWNER(cx, argc, vp, fnname, args, thisobj, frame, dbg) \ THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame); \ Debugger* dbg = Debugger::fromChildJSObject(thisobj)#define THIS_FRAME_OWNER_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter, dbg) \ THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter); \ Debugger* dbg = Debugger::fromChildJSObject(thisobj)/* static */boolDebuggerFrame::typeGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get type",args,frame);DebuggerFrameTypetype=DebuggerFrame::getType(frame);JSString*str;switch(type){caseDebuggerFrameType::Eval:str=cx->names().eval;break;caseDebuggerFrameType::Global:str=cx->names().global;break;caseDebuggerFrameType::Call:str=cx->names().call;break;caseDebuggerFrameType::Module:str=cx->names().module;break;caseDebuggerFrameType::WasmCall:str=cx->names().wasmcall;break;default:MOZ_CRASH("bad DebuggerFrameType value");}args.rval().setString(str);returntrue;}/* static */boolDebuggerFrame::implementationGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get implementation",args,frame);DebuggerFrameImplementationimplementation=DebuggerFrame::getImplementation(frame);constchar*s;switch(implementation){caseDebuggerFrameImplementation::Baseline:s="baseline";break;caseDebuggerFrameImplementation::Ion:s="ion";break;caseDebuggerFrameImplementation::Interpreter:s="interpreter";break;caseDebuggerFrameImplementation::Wasm:s="wasm";break;default:MOZ_CRASH("bad DebuggerFrameImplementation value");}JSAtom*str=Atomize(cx,s,strlen(s));if(!str)returnfalse;args.rval().setString(str);returntrue;}/* static */boolDebuggerFrame::environmentGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get environment",args,frame);RootedDebuggerEnvironmentresult(cx);if(!DebuggerFrame::getEnvironment(cx,frame,&result))returnfalse;args.rval().setObject(*result);returntrue;}/* static */boolDebuggerFrame::calleeGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get callee",args,frame);RootedDebuggerObjectresult(cx);if(!DebuggerFrame::getCallee(cx,frame,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* static */boolDebuggerFrame::generatorGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get callee",args,frame);args.rval().setBoolean(DebuggerFrame::getIsGenerator(frame));returntrue;}/* static */boolDebuggerFrame::constructingGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get callee",args,frame);boolresult;if(!DebuggerFrame::getIsConstructing(cx,frame,result))returnfalse;args.rval().setBoolean(result);returntrue;}/* static */boolDebuggerFrame::thisGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get this",args,frame);returnDebuggerFrame::getThis(cx,frame,args.rval());}/* static */boolDebuggerFrame::olderGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get older",args,frame);RootedDebuggerFrameresult(cx);if(!DebuggerFrame::getOlder(cx,frame,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */staticboolDebuggerArguments_getArg(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);int32_ti=args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();/* Check that the this value is an Arguments object. */RootedObjectargsobj(cx,NonNullObject(cx,args.thisv()));if(!argsobj)returnfalse;if(argsobj->getClass()!=&DebuggerArguments::class_){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Arguments","getArgument",argsobj->getClass()->name);returnfalse;}/* * Put the Debugger.Frame into the this-value slot, then use THIS_FRAME * to check that it is still live and get the fp. */args.setThis(argsobj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME));THIS_FRAME(cx,argc,vp,"get argument",ca2,thisobj,frame);// TODO handle wasm frame arguments -- they are not yet reflectable.MOZ_ASSERT(!frame.isWasmDebugFrame(),"a wasm frame args");/* * Since getters can be extracted and applied to other objects, * there is no guarantee this object has an ith argument. */MOZ_ASSERT(i>=0);RootedValuearg(cx);RootedScriptscript(cx);if(unsigned(i)<frame.numActualArgs()){script=frame.script();{AutoCompartmentac(cx,script);if(!script->ensureHasAnalyzedArgsUsage(cx))returnfalse;}if(unsigned(i)<frame.numFormalArgs()){for(PositionalFormalParameterIterfi(script);fi;fi++){if(fi.argumentSlot()==unsigned(i)){// We might've been called before the CallObject was// created.if(fi.closedOver()&&frame.hasInitialEnvironment())arg=frame.callObj().aliasedBinding(fi);elsearg=frame.unaliasedActual(i,DONT_CHECK_ALIASING);break;}}}elseif(script->argsObjAliasesFormals()&&frame.hasArgsObj()){arg=frame.argsObj().arg(i);}else{arg=frame.unaliasedActual(i,DONT_CHECK_ALIASING);}}else{arg.setUndefined();}if(!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx,&arg))returnfalse;args.rval().set(arg);returntrue;}/* static */DebuggerArguments*DebuggerArguments::create(JSContext*cx,HandleObjectproto,HandleDebuggerFrameframe){AbstractFramePtrreferent=DebuggerFrame::getReferent(frame);RootedNativeObjectobj(cx,NewNativeObjectWithGivenProto(cx,&DebuggerArguments::class_,proto));if(!obj)returnnullptr;SetReservedSlot(obj,FRAME_SLOT,ObjectValue(*frame));MOZ_ASSERT(referent.numActualArgs()<=0x7fffffff);unsignedfargc=referent.numActualArgs();RootedValuefargcVal(cx,Int32Value(fargc));if(!NativeDefineProperty(cx,obj,cx->names().length,fargcVal,nullptr,nullptr,JSPROP_PERMANENT|JSPROP_READONLY)){returnnullptr;}Rooted<jsid>id(cx);for(unsignedi=0;i<fargc;i++){RootedFunctiongetobj(cx);getobj=NewNativeFunction(cx,DebuggerArguments_getArg,0,nullptr,gc::AllocKind::FUNCTION_EXTENDED);if(!getobj)returnnullptr;id=INT_TO_JSID(i);if(!getobj||!NativeDefineProperty(cx,obj,id,UndefinedHandleValue,JS_DATA_TO_FUNC_PTR(GetterOp,getobj.get()),nullptr,JSPROP_ENUMERATE|JSPROP_SHARED|JSPROP_GETTER)){returnnullptr;}getobj->setExtendedSlot(0,Int32Value(i));}return&obj->as<DebuggerArguments>();}/* static */boolDebuggerFrame::argumentsGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get arguments",args,frame);RootedDebuggerArgumentsresult(cx);if(!DebuggerFrame::getArguments(cx,frame,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}staticboolDebuggerFrame_getScript(JSContext*cx,unsignedargc,Value*vp){THIS_FRAME(cx,argc,vp,"get script",args,thisobj,frame);Debugger*debug=Debugger::fromChildJSObject(thisobj);RootedObjectscriptObject(cx);if(frame.isFunctionFrame()){RootedFunctioncallee(cx,frame.callee());if(callee->isInterpreted()){RootedScriptscript(cx,callee->nonLazyScript());scriptObject=debug->wrapScript(cx,script);if(!scriptObject)returnfalse;}}elseif(frame.isWasmDebugFrame()){RootedWasmInstanceObjectinstance(cx,frame.wasmInstance()->object());scriptObject=debug->wrapWasmScript(cx,instance);if(!scriptObject)returnfalse;}else{/* * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script * frames. */RootedScriptscript(cx,frame.script());scriptObject=debug->wrapScript(cx,script);if(!scriptObject)returnfalse;}args.rval().setObjectOrNull(scriptObject);returntrue;}/* static */boolDebuggerFrame::offsetGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get offset",args,frame);size_tresult;if(!DebuggerFrame::getOffset(cx,frame,result))returnfalse;args.rval().setNumber(double(result));returntrue;}/* static */boolDebuggerFrame::liveGetter(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedDebuggerFrameframe(cx,DebuggerFrame_checkThis(cx,args,"get live",false));if(!frame)returnfalse;args.rval().setBoolean(frame->isLive());returntrue;}staticboolIsValidHook(constValue&v){returnv.isUndefined()||(v.isObject()&&v.toObject().isCallable());}/* static */boolDebuggerFrame::onStepGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get onStep",args,frame);OnStepHandler*handler=frame->onStepHandler();RootedValuevalue(cx,handler?ObjectOrNullValue(handler->object()):UndefinedValue());MOZ_ASSERT(IsValidHook(value));args.rval().set(value);returntrue;}/* static */boolDebuggerFrame::onStepSetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"set onStep",args,frame);if(!args.requireAtLeast(cx,"Debugger.Frame.set onStep",1))returnfalse;if(!IsValidHook(args[0])){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_CALLABLE_OR_UNDEFINED);returnfalse;}ScriptedOnStepHandler*handler=nullptr;if(!args[0].isUndefined()){handler=cx->new_<ScriptedOnStepHandler>(&args[0].toObject());if(!handler)returnfalse;}if(!DebuggerFrame::setOnStepHandler(cx,frame,handler)){handler->drop();returnfalse;}args.rval().setUndefined();returntrue;}/* static */boolDebuggerFrame::onPopGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"get onPop",args,frame);OnPopHandler*handler=frame->onPopHandler();RootedValuevalue(cx,handler?ObjectValue(*handler->object()):UndefinedValue());MOZ_ASSERT(IsValidHook(value));args.rval().set(value);returntrue;}/* static */boolDebuggerFrame::onPopSetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"set onPop",args,frame);if(!args.requireAtLeast(cx,"Debugger.Frame.set onPop",1))returnfalse;if(!IsValidHook(args[0])){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_CALLABLE_OR_UNDEFINED);returnfalse;}ScriptedOnPopHandler*handler=nullptr;if(!args[0].isUndefined()){handler=cx->new_<ScriptedOnPopHandler>(&args[0].toObject());if(!handler)returnfalse;}frame->setOnPopHandler(handler);args.rval().setUndefined();returntrue;}/* static */boolDebuggerFrame::evalMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"eval",args,frame);if(!args.requireAtLeast(cx,"Debugger.Frame.prototype.eval",1))returnfalse;AutoStableStringCharsstableChars(cx);if(!ValueToStableChars(cx,"Debugger.Frame.prototype.eval",args[0],stableChars))returnfalse;mozilla::Range<constchar16_t>chars=stableChars.twoByteRange();EvalOptionsoptions;if(!ParseEvalOptions(cx,args.get(1),options))returnfalse;JSTrapStatusstatus;RootedValuevalue(cx);if(!DebuggerFrame::eval(cx,frame,chars,nullptr,options,status,&value))returnfalse;returnframe->owner()->newCompletionValue(cx,status,value,args.rval());}/* static */boolDebuggerFrame::evalWithBindingsMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_FRAME(cx,argc,vp,"evalWithBindings",args,frame);if(!args.requireAtLeast(cx,"Debugger.Frame.prototype.evalWithBindings",2))returnfalse;AutoStableStringCharsstableChars(cx);if(!ValueToStableChars(cx,"Debugger.Frame.prototype.evalWithBindings",args[0],stableChars)){returnfalse;}mozilla::Range<constchar16_t>chars=stableChars.twoByteRange();RootedObjectbindings(cx,NonNullObject(cx,args[1]));if(!bindings)returnfalse;EvalOptionsoptions;if(!ParseEvalOptions(cx,args.get(2),options))returnfalse;JSTrapStatusstatus;RootedValuevalue(cx);if(!DebuggerFrame::eval(cx,frame,chars,bindings,options,status,&value))returnfalse;returnframe->owner()->newCompletionValue(cx,status,value,args.rval());}/* static */boolDebuggerFrame::construct(JSContext*cx,unsignedargc,Value*vp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NO_CONSTRUCTOR,"Debugger.Frame");returnfalse;}constJSPropertySpecDebuggerFrame::properties_[]={JS_PSG("arguments",DebuggerFrame::argumentsGetter,0),JS_PSG("callee",DebuggerFrame::calleeGetter,0),JS_PSG("constructing",DebuggerFrame::constructingGetter,0),JS_PSG("environment",DebuggerFrame::environmentGetter,0),JS_PSG("generator",DebuggerFrame::generatorGetter,0),JS_PSG("live",DebuggerFrame::liveGetter,0),JS_PSG("offset",DebuggerFrame::offsetGetter,0),JS_PSG("older",DebuggerFrame::olderGetter,0),JS_PSG("script",DebuggerFrame_getScript,0),JS_PSG("this",DebuggerFrame::thisGetter,0),JS_PSG("type",DebuggerFrame::typeGetter,0),JS_PSG("implementation",DebuggerFrame::implementationGetter,0),JS_PSGS("onStep",DebuggerFrame::onStepGetter,DebuggerFrame::onStepSetter,0),JS_PSGS("onPop",DebuggerFrame::onPopGetter,DebuggerFrame::onPopSetter,0),JS_PS_END};constJSFunctionSpecDebuggerFrame::methods_[]={JS_FN("eval",DebuggerFrame::evalMethod,1,0),JS_FN("evalWithBindings",DebuggerFrame::evalWithBindingsMethod,1,0),JS_FS_END};/*** Debugger.Object *****************************************************************************/voidDebuggerObject_trace(JSTracer*trc,JSObject*obj){/* * There is a barrier on private pointers, so the Unbarriered marking * is okay. */if(JSObject*referent=(JSObject*)obj->as<NativeObject>().getPrivate()){TraceManuallyBarrieredCrossCompartmentEdge(trc,obj,&referent,"Debugger.Object referent");obj->as<NativeObject>().setPrivateUnbarriered(referent);}}staticDebuggerObject*DebuggerObject_checkThis(JSContext*cx,constCallArgs&args,constchar*fnname){JSObject*thisobj=NonNullObject(cx,args.thisv());if(!thisobj)returnnullptr;if(thisobj->getClass()!=&DebuggerObject::class_){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Object",fnname,thisobj->getClass()->name);returnnullptr;}/* * Forbid Debugger.Object.prototype, which is of class DebuggerObject::class_ * but isn't a real working Debugger.Object. The prototype object is * distinguished by having no referent. */DebuggerObject*nthisobj=&thisobj->as<DebuggerObject>();if(!nthisobj->getPrivate()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Object",fnname,"prototype object");returnnullptr;}returnnthisobj;}#define THIS_DEBUGOBJECT(cx, argc, vp, fnname, args, object) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedDebuggerObject object(cx, DebuggerObject_checkThis(cx, args, fnname)); \ if (!object) \ return false; \#define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \ if (!obj) \ return false; \ obj = (JSObject*) obj->as<NativeObject>().getPrivate(); \ MOZ_ASSERT(obj)#define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \ CallArgs args = CallArgsFromVp(argc, vp); \ RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \ if (!obj) \ return false; \ Debugger* dbg = Debugger::fromChildJSObject(obj); \ obj = (JSObject*) obj->as<NativeObject>().getPrivate(); \ MOZ_ASSERT(obj)#define THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, fnname, args, obj) \ THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj); \ obj = CheckedUnwrap(obj); \ if (!obj) { \ ReportAccessDenied(cx); \ return false; \ } \ if (!obj->is<PromiseObject>()) { \ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,\ "Debugger", "Promise", obj->getClass()->name); \ return false; \ } \ Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());#define THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, fnname, args, dbg, obj) \ THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj); \ obj = CheckedUnwrap(obj); \ if (!obj) { \ ReportAccessDenied(cx); \ return false; \ } \ if (!obj->is<PromiseObject>()) { \ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,\ "Debugger", "Promise", obj->getClass()->name); \ return false; \ } \ Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());/* static */boolDebuggerObject::construct(JSContext*cx,unsignedargc,Value*vp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NO_CONSTRUCTOR,"Debugger.Object");returnfalse;}/* static */boolDebuggerObject::callableGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get callable",args,object)args.rval().setBoolean(object->isCallable());returntrue;}/* static */boolDebuggerObject::isBoundFunctionGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get isBoundFunction",args,object)if(!object->isDebuggeeFunction()){args.rval().setUndefined();returntrue;}args.rval().setBoolean(object->isBoundFunction());returntrue;}/* static */boolDebuggerObject::isArrowFunctionGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get isArrowFunction",args,object)if(!object->isDebuggeeFunction()){args.rval().setUndefined();returntrue;}args.rval().setBoolean(object->isArrowFunction());returntrue;}/* static */boolDebuggerObject::protoGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get proto",args,object)RootedDebuggerObjectresult(cx);if(!DebuggerObject::getPrototypeOf(cx,object,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* static */boolDebuggerObject::classGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get class",args,object)RootedStringresult(cx);if(!DebuggerObject::getClassName(cx,object,&result))returnfalse;args.rval().setString(result);returntrue;}/* static */boolDebuggerObject::nameGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get name",args,object)if(!object->isFunction()){args.rval().setUndefined();returntrue;}RootedStringresult(cx,object->name(cx));if(result)args.rval().setString(result);elseargs.rval().setUndefined();returntrue;}/* static */boolDebuggerObject::displayNameGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get displayName",args,object)if(!object->isFunction()){args.rval().setUndefined();returntrue;}RootedStringresult(cx,object->displayName(cx));if(result)args.rval().setString(result);elseargs.rval().setUndefined();returntrue;}/* static */boolDebuggerObject::parameterNamesGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get parameterNames",args,object)if(!object->isDebuggeeFunction()){args.rval().setUndefined();returntrue;}Rooted<StringVector>names(cx,StringVector(cx));if(!DebuggerObject::getParameterNames(cx,object,&names))returnfalse;RootedArrayObjectobj(cx,NewDenseFullyAllocatedArray(cx,names.length()));if(!obj)returnfalse;obj->ensureDenseInitializedLength(cx,0,names.length());for(size_ti=0;i<names.length();++i){Valuev;if(names[i])v=StringValue(names[i]);elsev=UndefinedValue();obj->setDenseElement(i,v);}args.rval().setObject(*obj);returntrue;}/* static */boolDebuggerObject::scriptGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT_OWNER_REFERENT(cx,argc,vp,"get script",args,dbg,obj);if(!obj->is<JSFunction>()){args.rval().setUndefined();returntrue;}RootedFunctionfun(cx,&obj->as<JSFunction>());if(!fun->isInterpreted()){args.rval().setUndefined();returntrue;}RootedScriptscript(cx,GetOrCreateFunctionScript(cx,fun));if(!script)returnfalse;/* Only hand out debuggee scripts. */if(!dbg->observesScript(script)){args.rval().setNull();returntrue;}RootedObjectscriptObject(cx,dbg->wrapScript(cx,script));if(!scriptObject)returnfalse;args.rval().setObject(*scriptObject);returntrue;}/* static */boolDebuggerObject::environmentGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT_OWNER_REFERENT(cx,argc,vp,"get environment",args,dbg,obj);/* Don't bother switching compartments just to check obj's type and get its env. */if(!obj->is<JSFunction>()||!obj->as<JSFunction>().isInterpreted()){args.rval().setUndefined();returntrue;}/* Only hand out environments of debuggee functions. */if(!dbg->observesGlobal(&obj->global())){args.rval().setNull();returntrue;}Rooted<Env*>env(cx);{AutoCompartmentac(cx,obj);RootedFunctionfun(cx,&obj->as<JSFunction>());env=GetDebugEnvironmentForFunction(cx,fun);if(!env)returnfalse;}returndbg->wrapEnvironment(cx,env,args.rval());}/* static */boolDebuggerObject::boundTargetFunctionGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get boundTargetFunction",args,object)if(!object->isDebuggeeFunction()||!object->isBoundFunction()){args.rval().setUndefined();returntrue;}RootedDebuggerObjectresult(cx);if(!DebuggerObject::getBoundTargetFunction(cx,object,&result))returnfalse;args.rval().setObject(*result);returntrue;}/* static */boolDebuggerObject::boundThisGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get boundThis",args,object)if(!object->isDebuggeeFunction()||!object->isBoundFunction()){args.rval().setUndefined();returntrue;}returnDebuggerObject::getBoundThis(cx,object,args.rval());}/* static */boolDebuggerObject::boundArgumentsGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get boundArguments",args,object)if(!object->isDebuggeeFunction()||!object->isBoundFunction()){args.rval().setUndefined();returntrue;}Rooted<ValueVector>result(cx,ValueVector(cx));if(!DebuggerObject::getBoundArguments(cx,object,&result))returnfalse;RootedObjectobj(cx,NewDenseCopiedArray(cx,result.length(),result.begin()));if(!obj)returnfalse;args.rval().setObject(*obj);returntrue;}/* static */boolDebuggerObject::globalGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get global",args,object)RootedDebuggerObjectresult(cx);if(!DebuggerObject::getGlobal(cx,object,&result))returnfalse;args.rval().setObject(*result);returntrue;}/* static */boolDebuggerObject::allocationSiteGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get allocationSite",args,object)RootedObjectresult(cx);if(!DebuggerObject::getAllocationSite(cx,object,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}// Returns the "name" field (see js.msg), which may be used as a unique// identifier, for any error object with a JSErrorReport or undefined// if the object has no JSErrorReport./* static */boolDebuggerObject::errorMessageNameGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get errorMessageName",args,object)RootedStringresult(cx);if(!DebuggerObject::getErrorMessageName(cx,object,&result))returnfalse;if(result)args.rval().setString(result);elseargs.rval().setUndefined();returntrue;}/* static */boolDebuggerObject::errorNotesGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get errorNotes",args,object)returnDebuggerObject::getErrorNotes(cx,object,args.rval());}/* static */boolDebuggerObject::errorLineNumberGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get errorLineNumber",args,object)returnDebuggerObject::getErrorLineNumber(cx,object,args.rval());}/* static */boolDebuggerObject::errorColumnNumberGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get errorColumnNumber",args,object)returnDebuggerObject::getErrorColumnNumber(cx,object,args.rval());}/* static */boolDebuggerObject::isProxyGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get isProxy",args,object)args.rval().setBoolean(object->isScriptedProxy());returntrue;}/* static */boolDebuggerObject::proxyTargetGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get proxyTarget",args,object)if(!object->isScriptedProxy()){args.rval().setUndefined();returntrue;}Rooted<DebuggerObject*>result(cx);if(!DebuggerObject::getScriptedProxyTarget(cx,object,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* static */boolDebuggerObject::proxyHandlerGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get proxyHandler",args,object)if(!object->isScriptedProxy()){args.rval().setUndefined();returntrue;}Rooted<DebuggerObject*>result(cx);if(!DebuggerObject::getScriptedProxyHandler(cx,object,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* static */boolDebuggerObject::isPromiseGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get isPromise",args,object)args.rval().setBoolean(object->isPromise());returntrue;}/* static */boolDebuggerObject::promiseStateGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get promiseState",args,object);if(!DebuggerObject::requirePromise(cx,object))returnfalse;RootedValueresult(cx);switch(object->promiseState()){caseJS::PromiseState::Pending:result.setString(cx->names().pending);break;caseJS::PromiseState::Fulfilled:result.setString(cx->names().fulfilled);break;caseJS::PromiseState::Rejected:result.setString(cx->names().rejected);break;}args.rval().set(result);returntrue;}/* static */boolDebuggerObject::promiseValueGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get promiseValue",args,object);if(!DebuggerObject::requirePromise(cx,object))returnfalse;if(object->promiseState()!=JS::PromiseState::Fulfilled){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_PROMISE_NOT_FULFILLED);returnfalse;}returnDebuggerObject::getPromiseValue(cx,object,args.rval());;}/* static */boolDebuggerObject::promiseReasonGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get promiseReason",args,object);if(!DebuggerObject::requirePromise(cx,object))returnfalse;if(object->promiseState()!=JS::PromiseState::Rejected){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_PROMISE_NOT_REJECTED);returnfalse;}returnDebuggerObject::getPromiseReason(cx,object,args.rval());;}/* static */boolDebuggerObject::promiseLifetimeGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get promiseLifetime",args,object);if(!DebuggerObject::requirePromise(cx,object))returnfalse;args.rval().setNumber(object->promiseLifetime());returntrue;}/* static */boolDebuggerObject::promiseTimeToResolutionGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"get promiseTimeToResolution",args,object);if(!DebuggerObject::requirePromise(cx,object))returnfalse;if(object->promiseState()==JS::PromiseState::Pending){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_PROMISE_NOT_RESOLVED);returnfalse;}args.rval().setNumber(object->promiseTimeToResolution());returntrue;}/* static */boolDebuggerObject::promiseAllocationSiteGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT_PROMISE(cx,argc,vp,"get promiseAllocationSite",args,refobj);RootedObjectallocSite(cx,promise->allocationSite());if(!allocSite){args.rval().setNull();returntrue;}if(!cx->compartment()->wrap(cx,&allocSite))returnfalse;args.rval().set(ObjectValue(*allocSite));returntrue;}/* static */boolDebuggerObject::promiseResolutionSiteGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT_PROMISE(cx,argc,vp,"get promiseResolutionSite",args,refobj);if(promise->state()==JS::PromiseState::Pending){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_PROMISE_NOT_RESOLVED);returnfalse;}RootedObjectresolutionSite(cx,promise->resolutionSite());if(!resolutionSite){args.rval().setNull();returntrue;}if(!cx->compartment()->wrap(cx,&resolutionSite))returnfalse;args.rval().set(ObjectValue(*resolutionSite));returntrue;}/* static */boolDebuggerObject::promiseIDGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT_PROMISE(cx,argc,vp,"get promiseID",args,refobj);args.rval().setNumber(double(promise->getID()));returntrue;}/* static */boolDebuggerObject::promiseDependentPromisesGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT_OWNER_PROMISE(cx,argc,vp,"get promiseDependentPromises",args,dbg,refobj);Rooted<GCVector<Value>>values(cx,GCVector<Value>(cx));{JSAutoCompartmentac(cx,promise);if(!promise->dependentPromises(cx,&values))returnfalse;}for(size_ti=0;i<values.length();i++){if(!dbg->wrapDebuggeeValue(cx,values[i]))returnfalse;}RootedArrayObjectpromises(cx);if(values.length()==0)promises=NewDenseEmptyArray(cx);elsepromises=NewDenseCopiedArray(cx,values.length(),values[0].address());if(!promises)returnfalse;args.rval().setObject(*promises);returntrue;}/* static */boolDebuggerObject::isExtensibleMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"isExtensible",args,object)boolresult;if(!DebuggerObject::isExtensible(cx,object,result))returnfalse;args.rval().setBoolean(result);returntrue;}/* static */boolDebuggerObject::isSealedMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"isSealed",args,object)boolresult;if(!DebuggerObject::isSealed(cx,object,result))returnfalse;args.rval().setBoolean(result);returntrue;}/* static */boolDebuggerObject::isFrozenMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"isFrozen",args,object)boolresult;if(!DebuggerObject::isFrozen(cx,object,result))returnfalse;args.rval().setBoolean(result);returntrue;}staticJSObject*IdVectorToArray(JSContext*cx,Handle<IdVector>ids){Rooted<ValueVector>vals(cx,ValueVector(cx));if(!vals.growBy(ids.length()))returnnullptr;for(size_ti=0,len=ids.length();i<len;i++){jsidid=ids[i];if(JSID_IS_INT(id)){JSString*str=Int32ToString<CanGC>(cx,JSID_TO_INT(id));if(!str)returnnullptr;vals[i].setString(str);}elseif(JSID_IS_ATOM(id)){vals[i].setString(JSID_TO_STRING(id));}elseif(JSID_IS_SYMBOL(id)){vals[i].setSymbol(JSID_TO_SYMBOL(id));}else{MOZ_ASSERT_UNREACHABLE("IdVector must contain only string, int, and Symbol jsids");}}returnNewDenseCopiedArray(cx,vals.length(),vals.begin());}/* static */boolDebuggerObject::getOwnPropertyNamesMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"getOwnPropertyNames",args,object)Rooted<IdVector>ids(cx,IdVector(cx));if(!DebuggerObject::getOwnPropertyNames(cx,object,&ids))returnfalse;RootedObjectobj(cx,IdVectorToArray(cx,ids));if(!obj)returnfalse;args.rval().setObject(*obj);returntrue;}/* static */boolDebuggerObject::getOwnPropertySymbolsMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"getOwnPropertySymbols",args,object)Rooted<IdVector>ids(cx,IdVector(cx));if(!DebuggerObject::getOwnPropertySymbols(cx,object,&ids))returnfalse;RootedObjectobj(cx,IdVectorToArray(cx,ids));if(!obj)returnfalse;args.rval().setObject(*obj);returntrue;}/* static */boolDebuggerObject::getOwnPropertyDescriptorMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"getOwnPropertyDescriptor",args,object)RootedIdid(cx);if(!ValueToId<CanGC>(cx,args.get(0),&id))returnfalse;Rooted<PropertyDescriptor>desc(cx);if(!DebuggerObject::getOwnPropertyDescriptor(cx,object,id,&desc))returnfalse;returnJS::FromPropertyDescriptor(cx,desc,args.rval());}/* static */boolDebuggerObject::preventExtensionsMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"preventExtensions",args,object)if(!DebuggerObject::preventExtensions(cx,object))returnfalse;args.rval().setUndefined();returntrue;}/* static */boolDebuggerObject::sealMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"seal",args,object)if(!DebuggerObject::seal(cx,object))returnfalse;args.rval().setUndefined();returntrue;}/* static */boolDebuggerObject::freezeMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"freeze",args,object)if(!DebuggerObject::freeze(cx,object))returnfalse;args.rval().setUndefined();returntrue;}/* static */boolDebuggerObject::definePropertyMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"defineProperty",args,object)if(!args.requireAtLeast(cx,"Debugger.Object.defineProperty",2))returnfalse;RootedIdid(cx);if(!ValueToId<CanGC>(cx,args[0],&id))returnfalse;Rooted<PropertyDescriptor>desc(cx);if(!ToPropertyDescriptor(cx,args[1],false,&desc))returnfalse;if(!DebuggerObject::defineProperty(cx,object,id,desc))returnfalse;args.rval().setUndefined();returntrue;}/* static */boolDebuggerObject::definePropertiesMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"defineProperties",args,object);if(!args.requireAtLeast(cx,"Debugger.Object.defineProperties",1))returnfalse;RootedValuearg(cx,args[0]);RootedObjectprops(cx,ToObject(cx,arg));if(!props)returnfalse;AutoIdVectorids(cx);Rooted<PropertyDescriptorVector>descs(cx,PropertyDescriptorVector(cx));if(!ReadPropertyDescriptors(cx,props,false,&ids,&descs))returnfalse;Rooted<IdVector>ids2(cx,IdVector(cx));if(!ids2.append(ids.begin(),ids.end()))returnfalse;if(!DebuggerObject::defineProperties(cx,object,ids2,descs))returnfalse;args.rval().setUndefined();returntrue;}/* * This does a non-strict delete, as a matter of API design. The case where the * property is non-configurable isn't necessarily exceptional here. *//* static */boolDebuggerObject::deletePropertyMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"deleteProperty",args,object)RootedIdid(cx);if(!ValueToId<CanGC>(cx,args.get(0),&id))returnfalse;ObjectOpResultresult;if(!DebuggerObject::deleteProperty(cx,object,id,result))returnfalse;args.rval().setBoolean(result.ok());returntrue;}/* static */boolDebuggerObject::callMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"call",callArgs,object);RootedValuethisv(cx,callArgs.get(0));Rooted<ValueVector>args(cx,ValueVector(cx));if(callArgs.length()>=2){if(!args.growBy(callArgs.length()-1))returnfalse;for(size_ti=1;i<callArgs.length();++i)args[i-1].set(callArgs[i]);}returnobject->call(cx,object,thisv,args,callArgs.rval());}/* static */boolDebuggerObject::applyMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"apply",callArgs,object);RootedValuethisv(cx,callArgs.get(0));Rooted<ValueVector>args(cx,ValueVector(cx));if(callArgs.length()>=2&&!callArgs[1].isNullOrUndefined()){if(!callArgs[1].isObject()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_BAD_APPLY_ARGS,js_apply_str);returnfalse;}RootedObjectargsobj(cx,&callArgs[1].toObject());unsignedargc=0;if(!GetLengthProperty(cx,argsobj,&argc))returnfalse;argc=unsigned(Min(argc,ARGS_LENGTH_MAX));if(!args.growBy(argc)||!GetElements(cx,argsobj,argc,args.begin()))returnfalse;}returnobject->call(cx,object,thisv,args,callArgs.rval());}/* static */boolDebuggerObject::asEnvironmentMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT_OWNER_REFERENT(cx,argc,vp,"asEnvironment",args,dbg,referent);if(!RequireGlobalObject(cx,args.thisv(),referent))returnfalse;Rooted<Env*>env(cx);{AutoCompartmentac(cx,referent);env=GetDebugEnvironmentForGlobalLexicalEnvironment(cx);if(!env)returnfalse;}returndbg->wrapEnvironment(cx,env,args.rval());}// Lookup a binding on the referent's global scope and change it to undefined// if it is an uninitialized lexical, otherwise do nothing. The method's// JavaScript return value is true _only_ when an uninitialized lexical has been// altered, otherwise it is false./* static */boolDebuggerObject::forceLexicalInitializationByNameMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"forceLexicalInitializationByName",args,object)if(!args.requireAtLeast(cx,"Debugger.Object.prototype.forceLexicalInitializationByName",1))returnfalse;if(!DebuggerObject::requireGlobal(cx,object))returnfalse;RootedIdid(cx);if(!ValueToIdentifier(cx,args[0],&id))returnfalse;boolresult;if(!DebuggerObject::forceLexicalInitializationByName(cx,object,id,result))returnfalse;args.rval().setBoolean(result);returntrue;}/* static */boolDebuggerObject::executeInGlobalMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"executeInGlobal",args,object);if(!args.requireAtLeast(cx,"Debugger.Object.prototype.executeInGlobal",1))returnfalse;if(!DebuggerObject::requireGlobal(cx,object))returnfalse;AutoStableStringCharsstableChars(cx);if(!ValueToStableChars(cx,"Debugger.Object.prototype.executeInGlobal",args[0],stableChars)){returnfalse;}mozilla::Range<constchar16_t>chars=stableChars.twoByteRange();EvalOptionsoptions;if(!ParseEvalOptions(cx,args.get(1),options))returnfalse;JSTrapStatusstatus;RootedValuevalue(cx);if(!DebuggerObject::executeInGlobal(cx,object,chars,nullptr,options,status,&value))returnfalse;returnobject->owner()->newCompletionValue(cx,status,value,args.rval());}/* static */boolDebuggerObject::executeInGlobalWithBindingsMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"executeInGlobalWithBindings",args,object);if(!args.requireAtLeast(cx,"Debugger.Object.prototype.executeInGlobalWithBindings",2))returnfalse;if(!DebuggerObject::requireGlobal(cx,object))returnfalse;AutoStableStringCharsstableChars(cx);if(!ValueToStableChars(cx,"Debugger.Object.prototype.executeInGlobalWithBindings",args[0],stableChars)){returnfalse;}mozilla::Range<constchar16_t>chars=stableChars.twoByteRange();RootedObjectbindings(cx,NonNullObject(cx,args[1]));if(!bindings)returnfalse;EvalOptionsoptions;if(!ParseEvalOptions(cx,args.get(2),options))returnfalse;JSTrapStatusstatus;RootedValuevalue(cx);if(!DebuggerObject::executeInGlobal(cx,object,chars,bindings,options,status,&value))returnfalse;returnobject->owner()->newCompletionValue(cx,status,value,args.rval());}/* static */boolDebuggerObject::makeDebuggeeValueMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"makeDebuggeeValue",args,object);if(!args.requireAtLeast(cx,"Debugger.Object.prototype.makeDebuggeeValue",1))returnfalse;returnDebuggerObject::makeDebuggeeValue(cx,object,args[0],args.rval());}/* static */boolDebuggerObject::unsafeDereferenceMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"unsafeDereference",args,object);RootedObjectresult(cx);if(!DebuggerObject::unsafeDereference(cx,object,&result))returnfalse;args.rval().setObject(*result);returntrue;}/* static */boolDebuggerObject::unwrapMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGOBJECT(cx,argc,vp,"unwrap",args,object);RootedDebuggerObjectresult(cx);if(!DebuggerObject::unwrap(cx,object,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}constJSPropertySpecDebuggerObject::properties_[]={JS_PSG("callable",DebuggerObject::callableGetter,0),JS_PSG("isBoundFunction",DebuggerObject::isBoundFunctionGetter,0),JS_PSG("isArrowFunction",DebuggerObject::isArrowFunctionGetter,0),JS_PSG("proto",DebuggerObject::protoGetter,0),JS_PSG("class",DebuggerObject::classGetter,0),JS_PSG("name",DebuggerObject::nameGetter,0),JS_PSG("displayName",DebuggerObject::displayNameGetter,0),JS_PSG("parameterNames",DebuggerObject::parameterNamesGetter,0),JS_PSG("script",DebuggerObject::scriptGetter,0),JS_PSG("environment",DebuggerObject::environmentGetter,0),JS_PSG("boundTargetFunction",DebuggerObject::boundTargetFunctionGetter,0),JS_PSG("boundThis",DebuggerObject::boundThisGetter,0),JS_PSG("boundArguments",DebuggerObject::boundArgumentsGetter,0),JS_PSG("global",DebuggerObject::globalGetter,0),JS_PSG("allocationSite",DebuggerObject::allocationSiteGetter,0),JS_PSG("errorMessageName",DebuggerObject::errorMessageNameGetter,0),JS_PSG("errorNotes",DebuggerObject::errorNotesGetter,0),JS_PSG("errorLineNumber",DebuggerObject::errorLineNumberGetter,0),JS_PSG("errorColumnNumber",DebuggerObject::errorColumnNumberGetter,0),JS_PSG("isProxy",DebuggerObject::isProxyGetter,0),JS_PSG("proxyTarget",DebuggerObject::proxyTargetGetter,0),JS_PSG("proxyHandler",DebuggerObject::proxyHandlerGetter,0),JS_PS_END};constJSPropertySpecDebuggerObject::promiseProperties_[]={JS_PSG("isPromise",DebuggerObject::isPromiseGetter,0),JS_PSG("promiseState",DebuggerObject::promiseStateGetter,0),JS_PSG("promiseValue",DebuggerObject::promiseValueGetter,0),JS_PSG("promiseReason",DebuggerObject::promiseReasonGetter,0),JS_PSG("promiseLifetime",DebuggerObject::promiseLifetimeGetter,0),JS_PSG("promiseTimeToResolution",DebuggerObject::promiseTimeToResolutionGetter,0),JS_PSG("promiseAllocationSite",DebuggerObject::promiseAllocationSiteGetter,0),JS_PSG("promiseResolutionSite",DebuggerObject::promiseResolutionSiteGetter,0),JS_PSG("promiseID",DebuggerObject::promiseIDGetter,0),JS_PSG("promiseDependentPromises",DebuggerObject::promiseDependentPromisesGetter,0),JS_PS_END};constJSFunctionSpecDebuggerObject::methods_[]={JS_FN("isExtensible",DebuggerObject::isExtensibleMethod,0,0),JS_FN("isSealed",DebuggerObject::isSealedMethod,0,0),JS_FN("isFrozen",DebuggerObject::isFrozenMethod,0,0),JS_FN("getOwnPropertyNames",DebuggerObject::getOwnPropertyNamesMethod,0,0),JS_FN("getOwnPropertySymbols",DebuggerObject::getOwnPropertySymbolsMethod,0,0),JS_FN("getOwnPropertyDescriptor",DebuggerObject::getOwnPropertyDescriptorMethod,1,0),JS_FN("preventExtensions",DebuggerObject::preventExtensionsMethod,0,0),JS_FN("seal",DebuggerObject::sealMethod,0,0),JS_FN("freeze",DebuggerObject::freezeMethod,0,0),JS_FN("defineProperty",DebuggerObject::definePropertyMethod,2,0),JS_FN("defineProperties",DebuggerObject::definePropertiesMethod,1,0),JS_FN("deleteProperty",DebuggerObject::deletePropertyMethod,1,0),JS_FN("call",DebuggerObject::callMethod,0,0),JS_FN("apply",DebuggerObject::applyMethod,0,0),JS_FN("asEnvironment",DebuggerObject::asEnvironmentMethod,0,0),JS_FN("forceLexicalInitializationByName",DebuggerObject::forceLexicalInitializationByNameMethod,1,0),JS_FN("executeInGlobal",DebuggerObject::executeInGlobalMethod,1,0),JS_FN("executeInGlobalWithBindings",DebuggerObject::executeInGlobalWithBindingsMethod,2,0),JS_FN("makeDebuggeeValue",DebuggerObject::makeDebuggeeValueMethod,1,0),JS_FN("unsafeDereference",DebuggerObject::unsafeDereferenceMethod,0,0),JS_FN("unwrap",DebuggerObject::unwrapMethod,0,0),JS_FS_END};/* static */NativeObject*DebuggerObject::initClass(JSContext*cx,HandleObjectobj,HandleObjectdebugCtor){Handle<GlobalObject*>global=obj.as<GlobalObject>();RootedObjectobjProto(cx,GlobalObject::getOrCreateObjectPrototype(cx,global));RootedNativeObjectobjectProto(cx,InitClass(cx,debugCtor,objProto,&class_,construct,0,properties_,methods_,nullptr,nullptr));if(!objectProto)returnnullptr;if(!DefinePropertiesAndFunctions(cx,objectProto,promiseProperties_,nullptr))returnnullptr;returnobjectProto;}/* static */DebuggerObject*DebuggerObject::create(JSContext*cx,HandleObjectproto,HandleObjectreferent,HandleNativeObjectdebugger){NewObjectKindnewKind=IsInsideNursery(referent)?GenericObject:TenuredObject;JSObject*obj=NewObjectWithGivenProto(cx,&DebuggerObject::class_,proto,newKind);if(!obj)returnnullptr;DebuggerObject&object=obj->as<DebuggerObject>();object.setPrivateGCThing(referent);object.setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER,ObjectValue(*debugger));return&object;}boolDebuggerObject::isCallable()const{returnreferent()->isCallable();}boolDebuggerObject::isFunction()const{returnreferent()->is<JSFunction>();}boolDebuggerObject::isDebuggeeFunction()const{returnreferent()->is<JSFunction>()&&owner()->observesGlobal(&referent()->as<JSFunction>().global());}boolDebuggerObject::isBoundFunction()const{MOZ_ASSERT(isDebuggeeFunction());returnreferent()->isBoundFunction();}boolDebuggerObject::isArrowFunction()const{MOZ_ASSERT(isDebuggeeFunction());returnreferent()->as<JSFunction>().isArrow();}boolDebuggerObject::isGlobal()const{returnreferent()->is<GlobalObject>();}boolDebuggerObject::isScriptedProxy()const{returnjs::IsScriptedProxy(referent());}boolDebuggerObject::isPromise()const{JSObject*referent=this->referent();if(IsCrossCompartmentWrapper(referent)){referent=CheckedUnwrap(referent);if(!referent)returnfalse;}returnreferent->is<PromiseObject>();}/* static */boolDebuggerObject::getClassName(JSContext*cx,HandleDebuggerObjectobject,MutableHandleStringresult){RootedObjectreferent(cx,object->referent());constchar*className;{AutoCompartmentac(cx,referent);className=GetObjectClassName(cx,referent);}JSAtom*str=Atomize(cx,className,strlen(className));if(!str)returnfalse;result.set(str);returntrue;}/* static */boolDebuggerObject::getGlobal(JSContext*cx,HandleDebuggerObjectobject,MutableHandleDebuggerObjectresult){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();RootedObjectglobal(cx,&referent->global());returndbg->wrapDebuggeeObject(cx,global,result);}JSAtom*DebuggerObject::name(JSContext*cx)const{MOZ_ASSERT(isFunction());JSAtom*atom=referent()->as<JSFunction>().explicitName();if(atom)cx->markAtom(atom);returnatom;}JSAtom*DebuggerObject::displayName(JSContext*cx)const{MOZ_ASSERT(isFunction());JSAtom*atom=referent()->as<JSFunction>().displayAtom();if(atom)cx->markAtom(atom);returnatom;}JS::PromiseStateDebuggerObject::promiseState()const{returnpromise()->state();}doubleDebuggerObject::promiseLifetime()const{returnpromise()->lifetime();}doubleDebuggerObject::promiseTimeToResolution()const{MOZ_ASSERT(promiseState()!=JS::PromiseState::Pending);returnpromise()->timeToResolution();}/* static */boolDebuggerObject::getParameterNames(JSContext*cx,HandleDebuggerObjectobject,MutableHandle<StringVector>result){MOZ_ASSERT(object->isDebuggeeFunction());RootedFunctionreferent(cx,&object->referent()->as<JSFunction>());if(!result.growBy(referent->nargs()))returnfalse;if(referent->isInterpreted()){RootedScriptscript(cx,GetOrCreateFunctionScript(cx,referent));if(!script)returnfalse;MOZ_ASSERT(referent->nargs()==script->numArgs());if(referent->nargs()>0){PositionalFormalParameterIterfi(script);for(size_ti=0;i<referent->nargs();i++,fi++){MOZ_ASSERT(fi.argumentSlot()==i);JSAtom*atom=fi.name();if(atom)cx->markAtom(atom);result[i].set(atom);}}}else{for(size_ti=0;i<referent->nargs();i++)result[i].set(nullptr);}returntrue;}/* static */boolDebuggerObject::getBoundTargetFunction(JSContext*cx,HandleDebuggerObjectobject,MutableHandleDebuggerObjectresult){MOZ_ASSERT(object->isBoundFunction());RootedFunctionreferent(cx,&object->referent()->as<JSFunction>());Debugger*dbg=object->owner();RootedObjecttarget(cx,referent->getBoundFunctionTarget());returndbg->wrapDebuggeeObject(cx,target,result);}/* static */boolDebuggerObject::getBoundThis(JSContext*cx,HandleDebuggerObjectobject,MutableHandleValueresult){MOZ_ASSERT(object->isBoundFunction());RootedFunctionreferent(cx,&object->referent()->as<JSFunction>());Debugger*dbg=object->owner();result.set(referent->getBoundFunctionThis());returndbg->wrapDebuggeeValue(cx,result);}/* static */boolDebuggerObject::getBoundArguments(JSContext*cx,HandleDebuggerObjectobject,MutableHandle<ValueVector>result){MOZ_ASSERT(object->isBoundFunction());RootedFunctionreferent(cx,&object->referent()->as<JSFunction>());Debugger*dbg=object->owner();size_tlength=referent->getBoundFunctionArgumentCount();if(!result.resize(length))returnfalse;for(size_ti=0;i<length;i++){result[i].set(referent->getBoundFunctionArgument(i));if(!dbg->wrapDebuggeeValue(cx,result[i]))returnfalse;}returntrue;}/* static */SavedFrame*Debugger::getObjectAllocationSite(JSObject&obj){JSObject*metadata=GetAllocationMetadata(&obj);if(!metadata)returnnullptr;MOZ_ASSERT(!metadata->is<WrapperObject>());returnSavedFrame::isSavedFrameAndNotProto(*metadata)?&metadata->as<SavedFrame>():nullptr;}/* static */boolDebuggerObject::getAllocationSite(JSContext*cx,HandleDebuggerObjectobject,MutableHandleObjectresult){RootedObjectreferent(cx,object->referent());RootedObjectallocSite(cx,Debugger::getObjectAllocationSite(*referent));if(!cx->compartment()->wrap(cx,&allocSite))returnfalse;result.set(allocSite);returntrue;}/* static */boolDebuggerObject::getErrorReport(JSContext*cx,HandleObjectmaybeError,JSErrorReport*&report){JSObject*obj=maybeError;if(IsCrossCompartmentWrapper(obj))obj=CheckedUnwrap(obj);if(!obj){ReportAccessDenied(cx);returnfalse;}if(!obj->is<ErrorObject>()){report=nullptr;returntrue;}report=obj->as<ErrorObject>().getErrorReport();returntrue;}/* static */boolDebuggerObject::getErrorMessageName(JSContext*cx,HandleDebuggerObjectobject,MutableHandleStringresult){RootedObjectreferent(cx,object->referent());JSErrorReport*report;if(!getErrorReport(cx,referent,report))returnfalse;if(!report){result.set(nullptr);returntrue;}constJSErrorFormatString*efs=GetErrorMessage(nullptr,report->errorNumber);if(!efs){result.set(nullptr);returntrue;}RootedStringstr(cx,JS_NewStringCopyZ(cx,efs->name));if(!cx->compartment()->wrap(cx,&str))returnfalse;result.set(str);returntrue;}/* static */boolDebuggerObject::getErrorNotes(JSContext*cx,HandleDebuggerObjectobject,MutableHandleValueresult){RootedObjectreferent(cx,object->referent());JSErrorReport*report;if(!getErrorReport(cx,referent,report))returnfalse;if(!report){result.setUndefined();returntrue;}RootedObjecterrorNotesArray(cx,CreateErrorNotesArray(cx,report));if(!errorNotesArray)returnfalse;if(!cx->compartment()->wrap(cx,&errorNotesArray))returnfalse;result.setObject(*errorNotesArray);returntrue;}/* static */boolDebuggerObject::getErrorLineNumber(JSContext*cx,HandleDebuggerObjectobject,MutableHandleValueresult){RootedObjectreferent(cx,object->referent());JSErrorReport*report;if(!getErrorReport(cx,referent,report))returnfalse;if(!report){result.setUndefined();returntrue;}result.setNumber(report->lineno);returntrue;}/* static */boolDebuggerObject::getErrorColumnNumber(JSContext*cx,HandleDebuggerObjectobject,MutableHandleValueresult){RootedObjectreferent(cx,object->referent());JSErrorReport*report;if(!getErrorReport(cx,referent,report))returnfalse;if(!report){result.setUndefined();returntrue;}result.setNumber(report->column);returntrue;}/* static */boolDebuggerObject::getPromiseValue(JSContext*cx,HandleDebuggerObjectobject,MutableHandleValueresult){MOZ_ASSERT(object->promiseState()==JS::PromiseState::Fulfilled);result.set(object->promise()->value());returnobject->owner()->wrapDebuggeeValue(cx,result);}/* static */boolDebuggerObject::getPromiseReason(JSContext*cx,HandleDebuggerObjectobject,MutableHandleValueresult){MOZ_ASSERT(object->promiseState()==JS::PromiseState::Rejected);result.set(object->promise()->reason());returnobject->owner()->wrapDebuggeeValue(cx,result);}/* static */boolDebuggerObject::isExtensible(JSContext*cx,HandleDebuggerObjectobject,bool&result){RootedObjectreferent(cx,object->referent());Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);returnIsExtensible(cx,referent,&result);}/* static */boolDebuggerObject::isSealed(JSContext*cx,HandleDebuggerObjectobject,bool&result){RootedObjectreferent(cx,object->referent());Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);returnTestIntegrityLevel(cx,referent,IntegrityLevel::Sealed,&result);}/* static */boolDebuggerObject::isFrozen(JSContext*cx,HandleDebuggerObjectobject,bool&result){RootedObjectreferent(cx,object->referent());Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);returnTestIntegrityLevel(cx,referent,IntegrityLevel::Frozen,&result);}/* static */boolDebuggerObject::getPrototypeOf(JSContext*cx,HandleDebuggerObjectobject,MutableHandleDebuggerObjectresult){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();RootedObjectproto(cx);{AutoCompartmentac(cx,referent);if(!GetPrototype(cx,referent,&proto))returnfalse;}if(!proto){result.set(nullptr);returntrue;}returndbg->wrapDebuggeeObject(cx,proto,result);}/* static */boolDebuggerObject::getOwnPropertyNames(JSContext*cx,HandleDebuggerObjectobject,MutableHandle<IdVector>result){RootedObjectreferent(cx,object->referent());AutoIdVectorids(cx);{Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);if(!GetPropertyKeys(cx,referent,JSITER_OWNONLY|JSITER_HIDDEN,&ids))returnfalse;}for(size_ti=0;i<ids.length();i++)cx->markId(ids[i]);returnresult.append(ids.begin(),ids.end());}/* static */boolDebuggerObject::getOwnPropertySymbols(JSContext*cx,HandleDebuggerObjectobject,MutableHandle<IdVector>result){RootedObjectreferent(cx,object->referent());AutoIdVectorids(cx);{Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);if(!GetPropertyKeys(cx,referent,JSITER_OWNONLY|JSITER_HIDDEN|JSITER_SYMBOLS|JSITER_SYMBOLSONLY,&ids))returnfalse;}for(size_ti=0;i<ids.length();i++)cx->markId(ids[i]);returnresult.append(ids.begin(),ids.end());}/* static */boolDebuggerObject::getOwnPropertyDescriptor(JSContext*cx,HandleDebuggerObjectobject,HandleIdid,MutableHandle<PropertyDescriptor>desc){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();/* Bug: This can cause the debuggee to run! */{Maybe<AutoCompartment>ac;ac.emplace(cx,referent);cx->markId(id);ErrorCopierec(ac);if(!GetOwnPropertyDescriptor(cx,referent,id,desc))returnfalse;}if(desc.object()){/* Rewrap the debuggee values in desc for the debugger. */if(!dbg->wrapDebuggeeValue(cx,desc.value()))returnfalse;if(desc.hasGetterObject()){RootedValueget(cx,ObjectOrNullValue(desc.getterObject()));if(!dbg->wrapDebuggeeValue(cx,&get))returnfalse;desc.setGetterObject(get.toObjectOrNull());}if(desc.hasSetterObject()){RootedValueset(cx,ObjectOrNullValue(desc.setterObject()));if(!dbg->wrapDebuggeeValue(cx,&set))returnfalse;desc.setSetterObject(set.toObjectOrNull());}// Avoid tripping same-compartment assertions in JS::FromPropertyDescriptor().desc.object().set(object);}returntrue;}/* static */boolDebuggerObject::preventExtensions(JSContext*cx,HandleDebuggerObjectobject){RootedObjectreferent(cx,object->referent());Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);returnPreventExtensions(cx,referent);}/* static */boolDebuggerObject::seal(JSContext*cx,HandleDebuggerObjectobject){RootedObjectreferent(cx,object->referent());Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);returnSetIntegrityLevel(cx,referent,IntegrityLevel::Sealed);}/* static */boolDebuggerObject::freeze(JSContext*cx,HandleDebuggerObjectobject){RootedObjectreferent(cx,object->referent());Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);returnSetIntegrityLevel(cx,referent,IntegrityLevel::Frozen);}/* static */boolDebuggerObject::defineProperty(JSContext*cx,HandleDebuggerObjectobject,HandleIdid,Handle<PropertyDescriptor>desc_){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();Rooted<PropertyDescriptor>desc(cx,desc_);if(!dbg->unwrapPropertyDescriptor(cx,referent,&desc))returnfalse;JS_TRY_OR_RETURN_FALSE(cx,CheckPropertyDescriptorAccessors(cx,desc));Maybe<AutoCompartment>ac;ac.emplace(cx,referent);if(!cx->compartment()->wrap(cx,&desc))returnfalse;cx->markId(id);ErrorCopierec(ac);if(!DefineProperty(cx,referent,id,desc))returnfalse;returntrue;}/* static */boolDebuggerObject::defineProperties(JSContext*cx,HandleDebuggerObjectobject,Handle<IdVector>ids,Handle<PropertyDescriptorVector>descs_){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();Rooted<PropertyDescriptorVector>descs(cx,PropertyDescriptorVector(cx));if(!descs.append(descs_.begin(),descs_.end()))returnfalse;for(size_ti=0;i<descs.length();i++){if(!dbg->unwrapPropertyDescriptor(cx,referent,descs[i]))returnfalse;JS_TRY_OR_RETURN_FALSE(cx,CheckPropertyDescriptorAccessors(cx,descs[i]));}Maybe<AutoCompartment>ac;ac.emplace(cx,referent);for(size_ti=0;i<descs.length();i++){if(!cx->compartment()->wrap(cx,descs[i]))returnfalse;cx->markId(ids[i]);}ErrorCopierec(ac);for(size_ti=0;i<descs.length();i++){if(!DefineProperty(cx,referent,ids[i],descs[i]))returnfalse;}returntrue;}/* static */boolDebuggerObject::deleteProperty(JSContext*cx,HandleDebuggerObjectobject,HandleIdid,ObjectOpResult&result){RootedObjectreferent(cx,object->referent());Maybe<AutoCompartment>ac;ac.emplace(cx,referent);cx->markId(id);ErrorCopierec(ac);returnDeleteProperty(cx,referent,id,result);}/* static */boolDebuggerObject::call(JSContext*cx,HandleDebuggerObjectobject,HandleValuethisv_,Handle<ValueVector>args,MutableHandleValueresult){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();if(!referent->isCallable()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Object","call",referent->getClass()->name);returnfalse;}RootedValuecalleev(cx,ObjectValue(*referent));/* * Unwrap Debugger.Objects. This happens in the debugger's compartment since * that is where any exceptions must be reported. */RootedValuethisv(cx,thisv_);if(!dbg->unwrapDebuggeeValue(cx,&thisv))returnfalse;Rooted<ValueVector>args2(cx,ValueVector(cx));if(!args2.append(args.begin(),args.end()))returnfalse;for(unsignedi=0;i<args2.length();++i){if(!dbg->unwrapDebuggeeValue(cx,args2[i]))returnfalse;}/* * Enter the debuggee compartment and rewrap all input value for that compartment. * (Rewrapping always takes place in the destination compartment.) */Maybe<AutoCompartment>ac;ac.emplace(cx,referent);if(!cx->compartment()->wrap(cx,&calleev)||!cx->compartment()->wrap(cx,&thisv))returnfalse;for(unsignedi=0;i<args2.length();++i){if(!cx->compartment()->wrap(cx,args2[i]))returnfalse;}/* * Call the function. Use receiveCompletionValue to return to the debugger * compartment and populate args.rval(). */LeaveDebuggeeNoExecutennx(cx);boolok;{InvokeArgsinvokeArgs(cx);ok=invokeArgs.init(cx,args2.length());if(ok){for(size_ti=0;i<args2.length();++i)invokeArgs[i].set(args2[i]);ok=js::Call(cx,calleev,thisv,invokeArgs,result);}}returndbg->receiveCompletionValue(ac,ok,result,result);}/* static */boolDebuggerObject::forceLexicalInitializationByName(JSContext*cx,HandleDebuggerObjectobject,HandleIdid,bool&result){if(!JSID_IS_STRING(id)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_EXPECTED_TYPE,"Debugger.Object.prototype.forceLexicalInitializationByName","string",InformalValueTypeName(IdToValue(id)));returnfalse;}MOZ_ASSERT(object->isGlobal());Rooted<GlobalObject*>referent(cx,&object->referent()->as<GlobalObject>());RootedObjectglobalLexical(cx,&referent->lexicalEnvironment());RootedObjectpobj(cx);Rooted<PropertyResult>prop(cx);if(!LookupProperty(cx,globalLexical,id,&pobj,&prop))returnfalse;result=false;if(prop){MOZ_ASSERT(prop.isNativeProperty());Shape*shape=prop.shape();Valuev=globalLexical->as<NativeObject>().getSlot(shape->slot());if(shape->hasSlot()&&v.isMagic()&&v.whyMagic()==JS_UNINITIALIZED_LEXICAL){globalLexical->as<NativeObject>().setSlot(shape->slot(),UndefinedValue());result=true;}}returntrue;}/* static */boolDebuggerObject::executeInGlobal(JSContext*cx,HandleDebuggerObjectobject,mozilla::Range<constchar16_t>chars,HandleObjectbindings,constEvalOptions&options,JSTrapStatus&status,MutableHandleValuevalue){MOZ_ASSERT(object->isGlobal());Rooted<GlobalObject*>referent(cx,&object->referent()->as<GlobalObject>());Debugger*dbg=object->owner();RootedObjectglobalLexical(cx,&referent->lexicalEnvironment());returnDebuggerGenericEval(cx,chars,bindings,options,status,value,dbg,globalLexical,nullptr);}/* static */boolDebuggerObject::makeDebuggeeValue(JSContext*cx,HandleDebuggerObjectobject,HandleValuevalue_,MutableHandleValueresult){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();RootedValuevalue(cx,value_);/* Non-objects are already debuggee values. */if(value.isObject()){// Enter this Debugger.Object's referent's compartment, and wrap the// argument as appropriate for references from there.{AutoCompartmentac(cx,referent);if(!cx->compartment()->wrap(cx,&value))returnfalse;}// Back in the debugger's compartment, produce a new Debugger.Object// instance referring to the wrapped argument.if(!dbg->wrapDebuggeeValue(cx,&value))returnfalse;}result.set(value);returntrue;}/* static */boolDebuggerObject::unsafeDereference(JSContext*cx,HandleDebuggerObjectobject,MutableHandleObjectresult){RootedObjectreferent(cx,object->referent());if(!cx->compartment()->wrap(cx,&referent))returnfalse;// Wrapping should return the WindowProxy.MOZ_ASSERT(!IsWindow(referent));result.set(referent);returntrue;}/* static */boolDebuggerObject::unwrap(JSContext*cx,HandleDebuggerObjectobject,MutableHandleDebuggerObjectresult){RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();RootedObjectunwrapped(cx,UnwrapOneChecked(referent));if(!unwrapped){result.set(nullptr);returntrue;}// Don't allow unwrapping to create a D.O whose referent is in an// invisible-to-Debugger global. (If our referent is a *wrapper* to such,// and the wrapper is in a visible compartment, that's fine.)JSCompartment*unwrappedCompartment=unwrapped->compartment();if(unwrappedCompartment->creationOptions().invisibleToDebugger()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_INVISIBLE_COMPARTMENT);returnfalse;}returndbg->wrapDebuggeeObject(cx,unwrapped,result);}/* static */boolDebuggerObject::requireGlobal(JSContext*cx,HandleDebuggerObjectobject){if(!object->isGlobal()){RootedObjectreferent(cx,object->referent());constchar*isWrapper="";constchar*isWindowProxy="";/* Help the poor programmer by pointing out wrappers around globals... */if(referent->is<WrapperObject>()){referent=js::UncheckedUnwrap(referent);isWrapper="a wrapper around ";}/* ... and WindowProxies around Windows. */if(IsWindowProxy(referent)){referent=ToWindowIfWindowProxy(referent);isWindowProxy="a WindowProxy referring to ";}RootedValuedbgobj(cx,ObjectValue(*object));if(referent->is<GlobalObject>()){ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_WRAPPER_IN_WAY,JSDVG_SEARCH_STACK,dbgobj,nullptr,isWrapper,isWindowProxy);}else{ReportValueErrorFlags(cx,JSREPORT_ERROR,JSMSG_DEBUG_BAD_REFERENT,JSDVG_SEARCH_STACK,dbgobj,nullptr,"a global object",nullptr);}returnfalse;}returntrue;}/* static */boolDebuggerObject::requirePromise(JSContext*cx,HandleDebuggerObjectobject){RootedObjectreferent(cx,object->referent());if(IsCrossCompartmentWrapper(referent)){referent=CheckedUnwrap(referent);if(!referent){ReportAccessDenied(cx);returnfalse;}}if(!referent->is<PromiseObject>()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_EXPECTED_TYPE,"Debugger","Promise",object->getClass()->name);returnfalse;}returntrue;}/* static */boolDebuggerObject::getScriptedProxyTarget(JSContext*cx,HandleDebuggerObjectobject,MutableHandleDebuggerObjectresult){MOZ_ASSERT(object->isScriptedProxy());RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();RootedObjectunwrapped(cx,js::GetProxyTargetObject(referent));if(!unwrapped){result.set(nullptr);returntrue;}returndbg->wrapDebuggeeObject(cx,unwrapped,result);}/* static */boolDebuggerObject::getScriptedProxyHandler(JSContext*cx,HandleDebuggerObjectobject,MutableHandleDebuggerObjectresult){MOZ_ASSERT(object->isScriptedProxy());RootedObjectreferent(cx,object->referent());Debugger*dbg=object->owner();RootedObjectunwrapped(cx,ScriptedProxyHandler::handlerObject(referent));if(!unwrapped){result.set(nullptr);returntrue;}returndbg->wrapDebuggeeObject(cx,unwrapped,result);}/*** Debugger.Environment ************************************************************************/voidDebuggerEnv_trace(JSTracer*trc,JSObject*obj){/* * There is a barrier on private pointers, so the Unbarriered marking * is okay. */if(Env*referent=(JSObject*)obj->as<NativeObject>().getPrivate()){TraceManuallyBarrieredCrossCompartmentEdge(trc,obj,&referent,"Debugger.Environment referent");obj->as<NativeObject>().setPrivateUnbarriered(referent);}}staticDebuggerEnvironment*DebuggerEnvironment_checkThis(JSContext*cx,constCallArgs&args,constchar*fnname,boolrequireDebuggee){JSObject*thisobj=NonNullObject(cx,args.thisv());if(!thisobj)returnnullptr;if(thisobj->getClass()!=&DebuggerEnvironment::class_){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Environment",fnname,thisobj->getClass()->name);returnnullptr;}/* * Forbid Debugger.Environment.prototype, which is of class DebuggerEnvironment::class_ * but isn't a real working Debugger.Environment. The prototype object is * distinguished by having no referent. */DebuggerEnvironment*nthisobj=&thisobj->as<DebuggerEnvironment>();if(!nthisobj->getPrivate()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INCOMPATIBLE_PROTO,"Debugger.Environment",fnname,"prototype object");returnnullptr;}/* * Forbid access to Debugger.Environment objects that are not debuggee * environments. */if(requireDebuggee){Rooted<Env*>env(cx,static_cast<Env*>(nthisobj->getPrivate()));if(!Debugger::fromChildJSObject(nthisobj)->observesGlobal(&env->global())){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_NOT_DEBUGGEE,"Debugger.Environment","environment");returnnullptr;}}returnnthisobj;}#define THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, fnname, args, environment) \ CallArgs args = CallArgsFromVp(argc, vp); \ Rooted<DebuggerEnvironment*> environment(cx, DebuggerEnvironment_checkThis(cx, args, fnname, false)); \ if (!environment) \ return false; \/* static */boolDebuggerEnvironment::construct(JSContext*cx,unsignedargc,Value*vp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NO_CONSTRUCTOR,"Debugger.Environment");returnfalse;}staticboolIsDeclarative(Env*env){returnenv->is<DebugEnvironmentProxy>()&&env->as<DebugEnvironmentProxy>().isForDeclarative();}template<typenameT>staticboolIsDebugEnvironmentWrapper(Env*env){returnenv->is<DebugEnvironmentProxy>()&&env->as<DebugEnvironmentProxy>().environment().is<T>();}boolDebuggerEnvironment::typeGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"get type",args,environment);if(!environment->requireDebuggee(cx))returnfalse;DebuggerEnvironmentTypetype=environment->type();constchar*s;switch(type){caseDebuggerEnvironmentType::Declarative:s="declarative";break;caseDebuggerEnvironmentType::With:s="with";break;caseDebuggerEnvironmentType::Object:s="object";break;}JSAtom*str=Atomize(cx,s,strlen(s),PinAtom);if(!str)returnfalse;args.rval().setString(str);returntrue;}/* static */boolDebuggerEnvironment::parentGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"get type",args,environment);if(!environment->requireDebuggee(cx))returnfalse;RootedDebuggerEnvironmentresult(cx);if(!environment->getParent(cx,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* static */boolDebuggerEnvironment::objectGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"get type",args,environment);if(!environment->requireDebuggee(cx))returnfalse;if(environment->type()==DebuggerEnvironmentType::Declarative){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_NO_ENV_OBJECT);returnfalse;}RootedDebuggerObjectresult(cx);if(!environment->getObject(cx,&result))returnfalse;args.rval().setObject(*result);returntrue;}/* static */boolDebuggerEnvironment::calleeGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"get callee",args,environment);if(!environment->requireDebuggee(cx))returnfalse;RootedDebuggerObjectresult(cx);if(!environment->getCallee(cx,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* static */boolDebuggerEnvironment::inspectableGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"get inspectable",args,environment);args.rval().setBoolean(environment->isDebuggee());returntrue;}/* static */boolDebuggerEnvironment::optimizedOutGetter(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"get optimizedOut",args,environment);args.rval().setBoolean(environment->isOptimized());returntrue;}/* static */boolDebuggerEnvironment::namesMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"names",args,environment);if(!environment->requireDebuggee(cx))returnfalse;Rooted<IdVector>ids(cx,IdVector(cx));if(!DebuggerEnvironment::getNames(cx,environment,&ids))returnfalse;RootedObjectobj(cx,IdVectorToArray(cx,ids));if(!obj)returnfalse;args.rval().setObject(*obj);returntrue;}/* static */boolDebuggerEnvironment::findMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"find",args,environment);if(!args.requireAtLeast(cx,"Debugger.Environment.find",1))returnfalse;if(!environment->requireDebuggee(cx))returnfalse;RootedIdid(cx);if(!ValueToIdentifier(cx,args[0],&id))returnfalse;RootedDebuggerEnvironmentresult(cx);if(!DebuggerEnvironment::find(cx,environment,id,&result))returnfalse;args.rval().setObjectOrNull(result);returntrue;}/* static */boolDebuggerEnvironment::getVariableMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"getVariable",args,environment);if(!args.requireAtLeast(cx,"Debugger.Environment.getVariable",1))returnfalse;if(!environment->requireDebuggee(cx))returnfalse;RootedIdid(cx);if(!ValueToIdentifier(cx,args[0],&id))returnfalse;returnDebuggerEnvironment::getVariable(cx,environment,id,args.rval());}/* static */boolDebuggerEnvironment::setVariableMethod(JSContext*cx,unsignedargc,Value*vp){THIS_DEBUGGER_ENVIRONMENT(cx,argc,vp,"setVariable",args,environment);if(!args.requireAtLeast(cx,"Debugger.Environment.setVariable",2))returnfalse;if(!environment->requireDebuggee(cx))returnfalse;RootedIdid(cx);if(!ValueToIdentifier(cx,args[0],&id))returnfalse;if(!DebuggerEnvironment::setVariable(cx,environment,id,args[1]))returnfalse;args.rval().setUndefined();returntrue;}boolDebuggerEnvironment::requireDebuggee(JSContext*cx)const{if(!isDebuggee()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_NOT_DEBUGGEE,"Debugger.Environment","environment");returnfalse;}returntrue;}constJSPropertySpecDebuggerEnvironment::properties_[]={JS_PSG("type",DebuggerEnvironment::typeGetter,0),JS_PSG("parent",DebuggerEnvironment::parentGetter,0),JS_PSG("object",DebuggerEnvironment::objectGetter,0),JS_PSG("callee",DebuggerEnvironment::calleeGetter,0),JS_PSG("inspectable",DebuggerEnvironment::inspectableGetter,0),JS_PSG("optimizedOut",DebuggerEnvironment::optimizedOutGetter,0),JS_PS_END};constJSFunctionSpecDebuggerEnvironment::methods_[]={JS_FN("names",DebuggerEnvironment::namesMethod,0,0),JS_FN("find",DebuggerEnvironment::findMethod,1,0),JS_FN("getVariable",DebuggerEnvironment::getVariableMethod,1,0),JS_FN("setVariable",DebuggerEnvironment::setVariableMethod,2,0),JS_FS_END};/* static */NativeObject*DebuggerEnvironment::initClass(JSContext*cx,HandleObjectdbgCtor,HandleObjectobj){Handle<GlobalObject*>global=obj.as<GlobalObject>();RootedObjectobjProto(cx,GlobalObject::getOrCreateObjectPrototype(cx,global));returnInitClass(cx,dbgCtor,objProto,&DebuggerEnvironment::class_,construct,0,properties_,methods_,nullptr,nullptr);}/* static */DebuggerEnvironment*DebuggerEnvironment::create(JSContext*cx,HandleObjectproto,HandleObjectreferent,HandleNativeObjectdebugger){NewObjectKindnewKind=IsInsideNursery(referent)?GenericObject:TenuredObject;RootedObjectobj(cx,NewObjectWithGivenProto(cx,&DebuggerEnvironment::class_,proto,newKind));if(!obj)returnnullptr;DebuggerEnvironment&environment=obj->as<DebuggerEnvironment>();environment.setPrivateGCThing(referent);environment.setReservedSlot(OWNER_SLOT,ObjectValue(*debugger));return&environment;}/* static */DebuggerEnvironmentTypeDebuggerEnvironment::type()const{/* Don't bother switching compartments just to check env's type. */if(IsDeclarative(referent()))returnDebuggerEnvironmentType::Declarative;if(IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent()))returnDebuggerEnvironmentType::With;returnDebuggerEnvironmentType::Object;}boolDebuggerEnvironment::getParent(JSContext*cx,MutableHandleDebuggerEnvironmentresult)const{/* Don't bother switching compartments just to get env's parent. */Rooted<Env*>parent(cx,referent()->enclosingEnvironment());if(!parent){result.set(nullptr);returntrue;}returnowner()->wrapEnvironment(cx,parent,result);}boolDebuggerEnvironment::getObject(JSContext*cx,MutableHandleDebuggerObjectresult)const{MOZ_ASSERT(type()!=DebuggerEnvironmentType::Declarative);/* Don't bother switching compartments just to get env's object. */RootedObjectobject(cx);if(IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent())){object.set(&referent()->as<DebugEnvironmentProxy>().environment().as<WithEnvironmentObject>().object());}elseif(IsDebugEnvironmentWrapper<NonSyntacticVariablesObject>(referent())){object.set(&referent()->as<DebugEnvironmentProxy>().environment().as<NonSyntacticVariablesObject>());}else{object.set(referent());MOZ_ASSERT(!object->is<DebugEnvironmentProxy>());}returnowner()->wrapDebuggeeObject(cx,object,result);}boolDebuggerEnvironment::getCallee(JSContext*cx,MutableHandleDebuggerObjectresult)const{if(!referent()->is<DebugEnvironmentProxy>()){result.set(nullptr);returntrue;}JSObject&scope=referent()->as<DebugEnvironmentProxy>().environment();if(!scope.is<CallObject>()){result.set(nullptr);returntrue;}RootedObjectcallee(cx,&scope.as<CallObject>().callee());if(IsInternalFunctionObject(*callee)){result.set(nullptr);returntrue;}returnowner()->wrapDebuggeeObject(cx,callee,result);}boolDebuggerEnvironment::isDebuggee()const{MOZ_ASSERT(referent());MOZ_ASSERT(!referent()->is<EnvironmentObject>());returnowner()->observesGlobal(&referent()->global());}boolDebuggerEnvironment::isOptimized()const{returnreferent()->is<DebugEnvironmentProxy>()&&referent()->as<DebugEnvironmentProxy>().isOptimizedOut();}/* static */boolDebuggerEnvironment::getNames(JSContext*cx,HandleDebuggerEnvironmentenvironment,MutableHandle<IdVector>result){MOZ_ASSERT(environment->isDebuggee());Rooted<Env*>referent(cx,environment->referent());AutoIdVectorids(cx);{Maybe<AutoCompartment>ac;ac.emplace(cx,referent);ErrorCopierec(ac);if(!GetPropertyKeys(cx,referent,JSITER_HIDDEN,&ids))returnfalse;}for(size_ti=0;i<ids.length();++i){jsidid=ids[i];if(JSID_IS_ATOM(id)&&IsIdentifier(JSID_TO_ATOM(id))){cx->markId(id);if(!result.append(id))returnfalse;}}returntrue;}/* static */boolDebuggerEnvironment::find(JSContext*cx,HandleDebuggerEnvironmentenvironment,HandleIdid,MutableHandleDebuggerEnvironmentresult){MOZ_ASSERT(environment->isDebuggee());Rooted<Env*>env(cx,environment->referent());Debugger*dbg=environment->owner();{Maybe<AutoCompartment>ac;ac.emplace(cx,env);cx->markId(id);/* This can trigger resolve hooks. */ErrorCopierec(ac);for(;env;env=env->enclosingEnvironment()){boolfound;if(!HasProperty(cx,env,id,&found))returnfalse;if(found)break;}}if(!env){result.set(nullptr);returntrue;}returndbg->wrapEnvironment(cx,env,result);}/* static */boolDebuggerEnvironment::getVariable(JSContext*cx,HandleDebuggerEnvironmentenvironment,HandleIdid,MutableHandleValueresult){MOZ_ASSERT(environment->isDebuggee());Rooted<Env*>referent(cx,environment->referent());Debugger*dbg=environment->owner();{Maybe<AutoCompartment>ac;ac.emplace(cx,referent);cx->markId(id);/* This can trigger getters. */ErrorCopierec(ac);boolfound;if(!HasProperty(cx,referent,id,&found))returnfalse;if(!found){result.setUndefined();returntrue;}// For DebugEnvironmentProxys, we get sentinel values for optimized out// slots and arguments instead of throwing (the default behavior).//// See wrapDebuggeeValue for how the sentinel values are wrapped.if(referent->is<DebugEnvironmentProxy>()){Rooted<DebugEnvironmentProxy*>env(cx,&referent->as<DebugEnvironmentProxy>());if(!DebugEnvironmentProxy::getMaybeSentinelValue(cx,env,id,result))returnfalse;}else{if(!GetProperty(cx,referent,referent,id,result))returnfalse;}}// When we've faked up scope chain objects for optimized-out scopes,// declarative environments may contain internal JSFunction objects, which// we shouldn't expose to the user.if(result.isObject()){RootedObjectobj(cx,&result.toObject());if(obj->is<JSFunction>()&&IsInternalFunctionObject(obj->as<JSFunction>()))result.setMagic(JS_OPTIMIZED_OUT);}returndbg->wrapDebuggeeValue(cx,result);}/* static */boolDebuggerEnvironment::setVariable(JSContext*cx,HandleDebuggerEnvironmentenvironment,HandleIdid,HandleValuevalue_){MOZ_ASSERT(environment->isDebuggee());Rooted<Env*>referent(cx,environment->referent());Debugger*dbg=environment->owner();RootedValuevalue(cx,value_);if(!dbg->unwrapDebuggeeValue(cx,&value))returnfalse;{Maybe<AutoCompartment>ac;ac.emplace(cx,referent);if(!cx->compartment()->wrap(cx,&value))returnfalse;cx->markId(id);/* This can trigger setters. */ErrorCopierec(ac);/* Make sure the environment actually has the specified binding. */boolfound;if(!HasProperty(cx,referent,id,&found))returnfalse;if(!found){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_DEBUG_VARIABLE_NOT_FOUND);returnfalse;}/* Just set the property. */if(!SetProperty(cx,referent,id,value))returnfalse;}returntrue;}/*** JS::dbg::Builder ****************************************************************************/Builder::Builder(JSContext*cx,js::Debugger*debugger):debuggerObject(cx,debugger->toJSObject().get()),debugger(debugger){}#if DEBUGvoidBuilder::assertBuilt(JSObject*obj){// We can't use assertSameCompartment here, because that is always keyed to// some JSContext's current compartment, whereas BuiltThings can be// constructed and assigned to without respect to any particular context;// the only constraint is that they should be in their debugger's compartment.MOZ_ASSERT_IF(obj,debuggerObject->compartment()==obj->compartment());}#endifboolBuilder::Object::definePropertyToTrusted(JSContext*cx,constchar*name,JS::MutableHandleValuetrusted){// We should have checked for false Objects before calling this.MOZ_ASSERT(value);JSAtom*atom=Atomize(cx,name,strlen(name));if(!atom)returnfalse;RootedIdid(cx,AtomToId(atom));returnDefineProperty(cx,value,id,trusted);}boolBuilder::Object::defineProperty(JSContext*cx,constchar*name,JS::HandleValuepropval_){AutoCompartmentac(cx,debuggerObject());RootedValuepropval(cx,propval_);if(!debugger()->wrapDebuggeeValue(cx,&propval))returnfalse;returndefinePropertyToTrusted(cx,name,&propval);}boolBuilder::Object::defineProperty(JSContext*cx,constchar*name,JS::HandleObjectpropval_){RootedValuepropval(cx,ObjectOrNullValue(propval_));returndefineProperty(cx,name,propval);}boolBuilder::Object::defineProperty(JSContext*cx,constchar*name,Builder::Object&propval_){AutoCompartmentac(cx,debuggerObject());RootedValuepropval(cx,ObjectOrNullValue(propval_.value));returndefinePropertyToTrusted(cx,name,&propval);}Builder::ObjectBuilder::newObject(JSContext*cx){AutoCompartmentac(cx,debuggerObject);RootedPlainObjectobj(cx,NewBuiltinClassInstance<PlainObject>(cx));// If the allocation failed, this will return a false Object, as the spec promises.returnObject(cx,*this,obj);}/*** JS::dbg::AutoEntryMonitor ******************************************************************/AutoEntryMonitor::AutoEntryMonitor(JSContext*cx):cx_(cx),savedMonitor_(cx->entryMonitor){cx->entryMonitor=this;}AutoEntryMonitor::~AutoEntryMonitor(){cx_->entryMonitor=savedMonitor_;}/*** Glue ****************************************************************************************/externJS_PUBLIC_API(bool)JS_DefineDebuggerObject(JSContext*cx,HandleObjectobj){RootedNativeObjectobjProto(cx),debugCtor(cx),debugProto(cx),frameProto(cx),scriptProto(cx),sourceProto(cx),objectProto(cx),envProto(cx),memoryProto(cx);RootedObjectdebuggeeWouldRunProto(cx);RootedValuedebuggeeWouldRunCtor(cx);Handle<GlobalObject*>global=obj.as<GlobalObject>();objProto=GlobalObject::getOrCreateObjectPrototype(cx,global);if(!objProto)returnfalse;debugProto=InitClass(cx,obj,objProto,&Debugger::class_,Debugger::construct,1,Debugger::properties,Debugger::methods,nullptr,Debugger::static_methods,debugCtor.address());if(!debugProto)returnfalse;frameProto=DebuggerFrame::initClass(cx,debugCtor,obj);if(!frameProto)returnfalse;scriptProto=InitClass(cx,debugCtor,objProto,&DebuggerScript_class,DebuggerScript_construct,0,DebuggerScript_properties,DebuggerScript_methods,nullptr,nullptr);if(!scriptProto)returnfalse;sourceProto=InitClass(cx,debugCtor,sourceProto,&DebuggerSource_class,DebuggerSource_construct,0,DebuggerSource_properties,DebuggerSource_methods,nullptr,nullptr);if(!sourceProto)returnfalse;objectProto=DebuggerObject::initClass(cx,obj,debugCtor);if(!objectProto)returnfalse;envProto=DebuggerEnvironment::initClass(cx,debugCtor,obj);if(!envProto)returnfalse;memoryProto=InitClass(cx,debugCtor,objProto,&DebuggerMemory::class_,DebuggerMemory::construct,0,DebuggerMemory::properties,DebuggerMemory::methods,nullptr,nullptr);if(!memoryProto)returnfalse;debuggeeWouldRunProto=GlobalObject::getOrCreateCustomErrorPrototype(cx,global,JSEXN_DEBUGGEEWOULDRUN);if(!debuggeeWouldRunProto)returnfalse;debuggeeWouldRunCtor=global->getConstructor(JSProto_DebuggeeWouldRun);RootedIddebuggeeWouldRunId(cx,NameToId(ClassName(JSProto_DebuggeeWouldRun,cx)));if(!DefineProperty(cx,debugCtor,debuggeeWouldRunId,debuggeeWouldRunCtor,nullptr,nullptr,0)){returnfalse;}debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO,ObjectValue(*frameProto));debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO,ObjectValue(*objectProto));debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO,ObjectValue(*scriptProto));debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SOURCE_PROTO,ObjectValue(*sourceProto));debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO,ObjectValue(*envProto));debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO,ObjectValue(*memoryProto));returntrue;}staticinlinevoidAssertIsPromise(JSContext*cx,HandleObjectpromise){MOZ_ASSERT(promise);assertSameCompartment(cx,promise);MOZ_ASSERT(strcmp(promise->getClass()->name,"Promise")==0);}JS_PUBLIC_API(void)JS::dbg::onNewPromise(JSContext*cx,HandleObjectpromise_){RootedObjectpromise(cx,promise_);if(IsWrapper(promise))promise=UncheckedUnwrap(promise);AutoCompartmentac(cx,promise);AssertIsPromise(cx,promise);Debugger::slowPathPromiseHook(cx,Debugger::OnNewPromise,promise);}JS_PUBLIC_API(void)JS::dbg::onPromiseSettled(JSContext*cx,HandleObjectpromise){AssertIsPromise(cx,promise);Debugger::slowPathPromiseHook(cx,Debugger::OnPromiseSettled,promise);}JS_PUBLIC_API(bool)JS::dbg::IsDebugger(JSObject&obj){JSObject*unwrapped=CheckedUnwrap(&obj);returnunwrapped&&js::GetObjectClass(unwrapped)==&Debugger::class_&&js::Debugger::fromJSObject(unwrapped)!=nullptr;}JS_PUBLIC_API(bool)JS::dbg::GetDebuggeeGlobals(JSContext*cx,JSObject&dbgObj,AutoObjectVector&vector){MOZ_ASSERT(IsDebugger(dbgObj));js::Debugger*dbg=js::Debugger::fromJSObject(CheckedUnwrap(&dbgObj));if(!vector.reserve(vector.length()+dbg->debuggees.count())){JS_ReportOutOfMemory(cx);returnfalse;}for(WeakGlobalObjectSet::Ranger=dbg->allDebuggees();!r.empty();r.popFront())vector.infallibleAppend(static_cast<JSObject*>(r.front()));returntrue;}#ifdef DEBUG/* static */boolDebugger::isDebuggerCrossCompartmentEdge(JSObject*obj,constgc::Cell*target){MOZ_ASSERT(target);autocls=obj->getClass();constgc::Cell*referent=nullptr;if(cls==&DebuggerScript_class){referent=GetScriptReferentCell(obj);}elseif(cls==&DebuggerSource_class){referent=GetSourceReferentRawObject(obj);}elseif(obj->is<DebuggerObject>()){referent=static_cast<gc::Cell*>(obj->as<DebuggerObject>().getPrivate());}elseif(obj->is<DebuggerEnvironment>()){referent=static_cast<gc::Cell*>(obj->as<DebuggerEnvironment>().getPrivate());}returnreferent==target;}#endif/*** JS::dbg::GarbageCollectionEvent **************************************************************/namespaceJS{namespacedbg{/* static */GarbageCollectionEvent::PtrGarbageCollectionEvent::Create(JSRuntime*rt,::js::gcstats::Statistics&stats,uint64_tgcNumber){autodata=rt->make_unique<GarbageCollectionEvent>(gcNumber);if(!data)returnnullptr;data->nonincrementalReason=stats.nonincrementalReason();for(auto&slice:stats.slices()){if(!data->reason){// There is only one GC reason for the whole cycle, but for legacy// reasons this data is stored and replicated on each slice. Each// slice used to have its own GCReason, but now they are all the// same.data->reason=gcreason::ExplainReason(slice.reason);MOZ_ASSERT(data->reason);}if(!data->collections.growBy(1))returnnullptr;data->collections.back().startTimestamp=slice.start;data->collections.back().endTimestamp=slice.end;}returndata;}staticboolDefineStringProperty(JSContext*cx,HandleObjectobj,PropertyName*propName,constchar*strVal){RootedValueval(cx,UndefinedValue());if(strVal){JSAtom*atomized=Atomize(cx,strVal,strlen(strVal));if(!atomized)returnfalse;val=StringValue(atomized);}returnDefineProperty(cx,obj,propName,val);}JSObject*GarbageCollectionEvent::toJSObject(JSContext*cx)const{RootedObjectobj(cx,NewBuiltinClassInstance<PlainObject>(cx));RootedValuegcCycleNumberVal(cx,NumberValue(majorGCNumber_));if(!obj||!DefineStringProperty(cx,obj,cx->names().nonincrementalReason,nonincrementalReason)||!DefineStringProperty(cx,obj,cx->names().reason,reason)||!DefineProperty(cx,obj,cx->names().gcCycleNumber,gcCycleNumberVal)){returnnullptr;}RootedArrayObjectslicesArray(cx,NewDenseEmptyArray(cx));if(!slicesArray)returnnullptr;TimeStamporiginTime=TimeStamp::ProcessCreation();size_tidx=0;for(autorange=collections.all();!range.empty();range.popFront()){RootedPlainObjectcollectionObj(cx,NewBuiltinClassInstance<PlainObject>(cx));if(!collectionObj)returnnullptr;RootedValuestart(cx),end(cx);start=NumberValue((range.front().startTimestamp-originTime).ToMilliseconds());end=NumberValue((range.front().endTimestamp-originTime).ToMilliseconds());if(!DefineProperty(cx,collectionObj,cx->names().startTimestamp,start)||!DefineProperty(cx,collectionObj,cx->names().endTimestamp,end)){returnnullptr;}RootedValuecollectionVal(cx,ObjectValue(*collectionObj));if(!DefineElement(cx,slicesArray,idx++,collectionVal))returnnullptr;}RootedValueslicesValue(cx,ObjectValue(*slicesArray));if(!DefineProperty(cx,obj,cx->names().collections,slicesValue))returnnullptr;returnobj;}JS_PUBLIC_API(bool)FireOnGarbageCollectionHook(JSContext*cx,JS::dbg::GarbageCollectionEvent::Ptr&&data){AutoObjectVectortriggered(cx);{// We had better not GC (and potentially get a dangling Debugger// pointer) while finding all Debuggers observing a debuggee that// participated in this GC.AutoCheckCannotGCnoGC;for(ZoneGroupsItergroup(cx->runtime());!group.done();group.next()){for(Debugger*dbg:group->debuggerList()){if(dbg->enabled&&dbg->observedGC(data->majorGCNumber())&&dbg->getHook(Debugger::OnGarbageCollection)){if(!triggered.append(dbg->object)){JS_ReportOutOfMemory(cx);returnfalse;}}}}}for(;!triggered.empty();triggered.popBack()){Debugger*dbg=Debugger::fromJSObject(triggered.back());dbg->fireOnGarbageCollectionHook(cx,data);MOZ_ASSERT(!cx->isExceptionPending());}returntrue;}}// namespace dbg}// namespace JS